- Making the Table Editable
- The Cell Renderer
- The Cell Editor
- Conclusion
The Cell Editor
Now that I have my value column displaying correctly, I need to change what happens when the cell is being edited to avoid incorrect user input. To do this I will extend the DefaultCellEditor:
class CurrencyCellEditor extends DefaultCellEditor { private NumberFormat currencyFormat; public CurrencyCellEditor(final JFormattedTextField tf, NumberFormat nf) { super(tf); currencyFormat = nf; tf.setFocusLostBehavior(JFormattedTextField.COMMIT); tf.setHorizontalAlignment(SwingConstants.RIGHT); tf.setBorder(null); delegate = new EditorDelegate() { public void setValue(Object param) { Double _value = (Double)param; if (_value == null) { tf.setValue(currencyFormat.format(0.0)); } else { double _d = _value.doubleValue(); String _format = currencyFormat.format(_d); tf.setValue(_format); } } public Object getCellEditorValue() { try { String _field = tf.getText(); Number _number = currencyFormat.parse(_field); double _parsed = _number.doubleValue(); Double d = new Double(_parsed); return d; } catch (ParseException e) { e.printStackTrace(); return new Double(0.0); } } }; } }
The editor is a bit more complicated than the renderer, as you can see. The only thing I'm doing is to create a new constructor that accepts a JFormattedTextField. Due to the way in which the DefaultCellEditor is designed, this object must be passed into the editor. First, I pass in the JFormattedTextField as a final variable, which is required so that I can reference it inside of the inner class that I will define. Next, I set a global reference to the NumberFormat that was used to define the formatter for this text field. Doing so makes it easier to format the data and guarantee that what the FormattedField thinks is acceptable data and what the editor thinks is acceptable will match. Next, I set a few formatting options on the field so that it will display in the expected way.
The most complex part of this editor is the Delegate, which handles putting data into the text field as well as extracting data from the field. EditorDelegate is an inner class to the DefaultCellEditor class, which I can reference because I am a child of that object. The two methods that I need to overload are getCellEditorValue() and setValue().
The setValue()method passes in an object that was received from the TableModel. Therefore, in this situation I know that setValue() will receive a double. Therefore I cast the object into a double, parse the double using the currencyFormatter, and call setValue() on the JFormattedTextField.
The method getCellEditorValue() is the exact opposite. First, I retrieve the String from the JFormattedTextField. Next, I parse that String into a Number using the same currencyFormatter. Ffinally, I return the Double initialized with the value returned from the currencyFormatter.
The rest of the editor is completely handled by the DefaultCellEditor. Because I am not changing any other functionality of the editor, this is the only code I need to write for the editor to function. The final step is to update the initializeTable() method with my new editor:
public JTable initializeTable() { JTable _table = new JTable(mtm); NumberFormat _format = NumberFormat.getCurrencyInstance(); MyCurrencyRenderer _renderer = new MyCurrencyRenderer(_format); _table.setDefaultRenderer(Double.class, _renderer); NumberFormatter _formatter = new NumberFormatter(_format); JFormattedTextField _field = new JFormattedTextField(_formatter); MyCurrencyEditor _editor = new MyCurrencyEditor(_field, _format); _table.setDefaultEditor(Double.class, _editor); return _table; }
Again, I am setting the default editor for the JTable because I know that the Widget has only one Double value. To set it for a specific column only, I do this:
public JTable initializeTable() { JTable _table = new JTable(mtm); NumberFormat _format = NumberFormat.getCurrencyInstance(); MyCurrencyRenderer _renderer = new MyCurrencyRenderer(_format); TableColumnModel _model = _table.getColumnModel(); TableColumn _column = _model.getColumn(1); _column.setCellRenderer(_renderer); NumberFormatter _formatter = new NumberFormatter(_format); JFormattedTextField _field = new JFormattedTextField(_formatter); MyCurrencyEditor _editor = new MyCurrencyEditor(_field, _format); _column.setCellEditor(_editor); return _table; }
Now I have a JTable that allows proper editing of the value column and will ignore improper input into that cell. In addition, currency will also be displayed properly based on the locale of the system on which it is running. These examples can be easily extended to data verification on all the columns to ensure that no bad data is entered into the Widgets.