9

You guys were so awesome in point me in the right direction on my last question and I have sort of an extension of my original question here:

How to set a JTable column as String and sort as Double?

As I now have my price column formatted as $###,##0.00 by using my custom cell renderer, I have now set up a JTextField Editor for the cell as well. The editing of the cell works just fine except for when the value is updated, the number format set in my custom renderer no longer seems to format the cell (I'm loosing the $ after edit is committed). Is this renderer not supposed to render the cells even after the initial display of the data?

I have tried to use the following with no luck:

((AbstractTableModel) table.getModel()).fireTableDataChanged();

I was hoping that this would force the table to revalidate and repaint the cells using the custom renderer to render the new values, but this unfortunately did not work...

Am I missing something... Obviously, but what?

Community
  • 1
  • 1
titanic_fanatic
  • 593
  • 2
  • 5
  • 13

1 Answers1

11

When your editor concludes, the table's editingStopped() method collects the new value via getCellEditorValue() and uses it to setValueAt() in the model. The model, in turn, should fireTableCellUpdated(), which will invoke the prescribed renderer. Extending the default should be enough to handle Number formatting. In other cases, it may be convenient to use an instance of your renderer as your editor component; this example shows a typical implementation.

Addendum: Here's a basic example using the default editor and renderer implementations.

Addendum: Thanks to helpful comments from @mKorbel, I've updated the example to select the cell's text for editing, as described in @camickr's article Table Select All Editor.

RenderEditNumber

package overflow;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.MouseEvent;
import java.text.NumberFormat;
import java.util.EventObject;
import javax.swing.DefaultCellEditor;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.text.JTextComponent;

/** @see http://stackoverflow.com/a/10067560/230513 */
public class RenderEditNumber extends JPanel {

    private NumberFormat nf = NumberFormat.getCurrencyInstance();

    public RenderEditNumber() {
        DefaultTableModel model = new DefaultTableModel(
            new String[]{"Amount"}, 0) {

            @Override
            public Class<?> getColumnClass(int columnIndex) {
                return Double.class;
            }
        };
        for (int i = 0; i < 16; i++) {
            model.addRow(new Object[]{Double.valueOf(i)});
        }
        JTable table = new JTable(model) {

            @Override // Always selectAll()
            public boolean editCellAt(int row, int column, EventObject e) {
                boolean result = super.editCellAt(row, column, e);
                final Component editor = getEditorComponent();
                if (editor == null || !(editor instanceof JTextComponent)) {
                    return result;
                }
                if (e instanceof MouseEvent) {
                    EventQueue.invokeLater(new Runnable() {

                        @Override
                        public void run() {
                            ((JTextComponent) editor).selectAll();
                        }
                    });
                } else {
                    ((JTextComponent) editor).selectAll();
                }
                return result;
            }
        };
        table.setPreferredScrollableViewportSize(new Dimension(123, 123));
        table.setDefaultRenderer(Double.class, new CurrencyRenderer(nf));
        table.setDefaultEditor(Double.class, new CurrencyEditor(nf));
        this.add(new JScrollPane(table));
    }

    private static class CurrencyRenderer extends DefaultTableCellRenderer {

        private NumberFormat formatter;

        public CurrencyRenderer(NumberFormat formatter) {
            this.formatter = formatter;
            this.setHorizontalAlignment(JLabel.RIGHT);
        }

        @Override
        public void setValue(Object value) {
            setText((value == null) ? "" : formatter.format(value));
        }
    }

    private static class CurrencyEditor extends DefaultCellEditor {

        private NumberFormat formatter;
        private JTextField textField;

        public CurrencyEditor(NumberFormat formatter) {
            super(new JTextField());
            this.formatter = formatter;
            this.textField = (JTextField) this.getComponent();
            textField.setHorizontalAlignment(JTextField.RIGHT);
            textField.setBorder(null);
        }

        @Override
        public Object getCellEditorValue() {
            try {
                return new Double(textField.getText());
            } catch (NumberFormatException e) {
                return Double.valueOf(0);
            }
        }

        @Override
        public Component getTableCellEditorComponent(JTable table,
            Object value, boolean isSelected, int row, int column) {
            textField.setText((value == null)
                ? "" : formatter.format((Double) value));
            return textField;
        }
    }

    private void display() {
        JFrame f = new JFrame("RenderEditNumber");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(this);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                new RenderEditNumber().display();
            }
        });
    }
}
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • +1 but I saw that before not working correctly if you want add, remove or edit value that exist, Editor returns 0.00 :-), I'd have to look at .... – mKorbel Apr 09 '12 at 14:39
  • Good point. For matrix input, the editor intentionally returns zero on `NumberFormatException`; `cancelCellEditing()` would be a more general alternative. – trashgod Apr 09 '12 at 17:42
  • this is same for JFormattedTextField or JSpinner as CellEditor, I'm sure that Rob have got ..., there you have to returning Document, Rob where are you :-) – mKorbel Apr 09 '12 at 17:48
  • I don't understand; I usually just hit escape to cancel editing. – trashgod Apr 09 '12 at 19:26
  • please see my code [here](http://stackoverflow.com/a/10078587/714968), there everything works, but if I put there CurrencyInstance instead of NumberInstance, then my code works for editing and your for select whole value and replace with a new..., no idea now why... – mKorbel Apr 09 '12 at 19:28
  • I'd have to dig into it. I see you are extending `AbstractCellEditor`; I was trying to leverage `DefaultCellEditor`. I should add that `Double` may not be a good choice for currency. :-) – trashgod Apr 09 '12 at 19:32
  • Currency has decimal places and can returns Double, then there is only Number :-), maybe – mKorbel Apr 09 '12 at 19:40
  • This [Q&A](http://stackoverflow.com/q/7531513/230513) exposes some of the default editor's `Number` handling. – trashgod Apr 09 '12 at 19:43
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/9874/discussion-between-trashgod-and-mkorbel) – trashgod Apr 09 '12 at 19:43
  • I have an answer, please see my post here – mKorbel Apr 09 '12 at 23:34
  • @mKorbel: I've updated the example; canceling via escape now works more gracefully. – trashgod Apr 10 '12 at 03:58