Search code examples
javaswingjtablenumber-formattingtablecelleditor

using JTable cell editor


I am not sure why the editor i am setting for my jtable is not being called. I used the editor example from another SO Question.

When i edit my jtable it simply edits as string... i expected it to accept only number values. may be some exception if i type any other text... but i dont think the editor is being called here when i edit my table.

import javax.swing.*;
import javax.swing.table.*;
import javax.swing.text.NumberFormatter;

import java.awt.*;
import java.awt.event.*;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.ParsePosition;
import java.util.Locale;

public class Grow extends JFrame {

private static final Object[][] rowData = {{"Hello", "World"}};
private static final Object[] columnNames = {"A", "B"};

private JTable table;
private DefaultTableModel model;

public Grow() {
     Container c = getContentPane();
     c.setLayout(new BorderLayout());

     model = new DefaultTableModel(rowData, columnNames);
     table = new JTable();
     table.setModel(model);
     c.add(new JScrollPane(table), BorderLayout.CENTER);
     JButton add = new JButton("Add");
     JButton delete = new JButton("Delete");
     c.add(add, BorderLayout.LINE_START);
     add.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent ae) {
             model.addRow(rowData[0]);
         }
     });

     table.setCellEditor(new NumberCellEditor());

     c.add(delete, BorderLayout.LINE_END);
     delete.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent ae) {
             if(table.getSelectedRow()>-1)
                 model.removeRow(table.getSelectedRow());
         }
     });

     setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     pack(); 
}

class NumberCellEditor extends DefaultCellEditor {
    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    public NumberCellEditor(){
        super(new JFormattedTextField());
    }

    @Override
     public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
        JFormattedTextField editor = (JFormattedTextField) super.getTableCellEditorComponent(table, value, isSelected, row, column);

        if (value!=null){
            DecimalFormat numberFormat = new DecimalFormat("#,##0.00;(#,##0.00)");
            editor.setFormatterFactory(new javax.swing.text.DefaultFormatterFactory(new javax.swing.text.NumberFormatter(numberFormat)));
            Number num = (Number) value;  
            String text = numberFormat.format(num);
            editor.setHorizontalAlignment(SwingConstants.RIGHT);
            editor.setText(text);
        }
        System.out.println(value);
        return editor;
    }

    /*@Override
    public boolean stopCellEditing() {
        try {
            // try to get the value
            //this.getCellEditorValue();
            return super.stopCellEditing();
        } catch (Exception ex) {
            return false;
        }

    }
    */

    @Override
    public Object getCellEditorValue() {
        // get content of textField
        String str = (String) super.getCellEditorValue();
        if (str == null) {
            return null;
        }

        if (str.length() == 0) {
            return null;
        }

        // try to parse a number
        try {
            ParsePosition pos = new ParsePosition(0);
            Number n = NumberFormat.getInstance().parse(str, pos);
            if (pos.getIndex() != str.length()) {
                throw new ParseException(
                        "parsing incomplete", pos.getIndex());
            }

            // return an instance of column class
            return new Float(n.floatValue());

        } catch (ParseException pex) {
            throw new RuntimeException(pex);
        }
    }
    }

public static void main(String[] args) {
    Grow g = new Grow();
    g.setLocationRelativeTo(null);
    g.setVisible(true);
}
}

Solution

    • use plain vanilla JTextField with DocumentFilter instead of JFormattedTextField as editors Component.

    • I cant comment something, attached start_point for JFormattedTextField with XxxFormat (important details are in offical Oracle tutorial, APIs How to use FormattedTextField, NumberFormat etc.)

    • you can to add InternationalFormatter for filtering only Numbers

    e.g.

    InternationalFormatter formatter = new InternationalFormatter(format);
    formatter.setAllowsInvalid(false);
    //formatter.setMinimum(0.0);
    //formatter.setMaximum(1000.00);
    

    screen_shot

    enter image description here

    from code

    import java.awt.Component;
    import java.awt.EventQueue;
    import java.math.RoundingMode;
    import java.text.DecimalFormat;
    import java.text.NumberFormat;
    import java.text.ParseException;
    import javax.swing.*;
    import javax.swing.table.TableCellEditor;
    import javax.swing.table.TableCellRenderer;
    import javax.swing.table.TableColumnModel;
    
    public class EditorAsRendererTableTest {
    
        public EditorAsRendererTableTest() {
            JTable table = new JTable(3, 2);
            TableColumnModel colModel = table.getColumnModel();
            colModel.getColumn(0).setCellEditor(new MyCellEditor());
            colModel.getColumn(0).setCellRenderer(new MyCellEditor());
            JFrame frame = new JFrame("Test");
            frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
            table.setPreferredScrollableViewportSize(table.getPreferredSize());
            frame.add(new JScrollPane(table));
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        }
    
        public static void main(String[] args) {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    new EditorAsRendererTableTest();
                }
            });
        }
    
        private class MyCellEditor extends AbstractCellEditor implements TableCellEditor, TableCellRenderer {
    
            private static final long serialVersionUID = 1L;
            private JFormattedTextField renderer;
            private JFormattedTextField editor;
            private NumberFormat format = DecimalFormat.getInstance();
    
            public MyCellEditor() {
                format.setMinimumFractionDigits(2);
                format.setMaximumFractionDigits(4);
                format.setRoundingMode(RoundingMode.HALF_UP);
                renderer = new JFormattedTextField(format);
                renderer.setBorder(null);
                editor = new JFormattedTextField(format);
            }
    
            @Override
            public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
                renderer.setValue(value);
                return renderer;
            }
    
            @Override
            public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
                editor.setValue(value);
                return editor;
            }
    
            @Override
            public boolean stopCellEditing() {
                try {
                    editor.commitEdit();
                } catch (ParseException e) {
                    return false;
                }
                return super.stopCellEditing();
            }
    
            @Override
            public Object getCellEditorValue() {
                return editor.getValue();
            }
        }
    }