Can a Jtable save data whenever a cell loses focus?

前端 未结 3 1833
予麋鹿
予麋鹿 2020-12-01 08:04

The high level: I have a JTable that the user can use to edit data.

Whenever the user presses Enter or Tab to finish editing, the data is saved (I\'m asusming that \

相关标签:
3条回答
  • 2020-12-01 08:20

    One of the simple solutions proposed

    table.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
    

    is good only for String columns. The problem is if I have, for example, Float type of the column being edited, enter an empty string in corresponding cell and then click on any other control of the window – Java throws NullPointerException in CellEditorRemover.propertyChange() method of JTable.java. It uses getCellEditor() call to stop or cancel editing but it returns null in this case. If the value entered is not empty or if I remove terminateEditOnFocusLost flag everything is fine. Probably, the situation described is a bug.

    I hope I can provide a solution based on one of the previous posts. It’s not so trivial as I supposed before but seems to me it works. I had to inherit my own cell editor from default cell editor and my own text field from JTextField which has FocusListener. This focus listener works fine when editing cell loses a focus, and a focus gained by another control of the window. But in the case of cell selection changes focus listener is “deaf”. That’s why I also have to remember previously valid value before editing start to restore it if the entered value will be invalid.

    See the code below. Tested with Double, Float and Integer, but I hope this will also work with Byte and String.

    Text field with focus listener:

    public class TextFieldCell extends JTextField {
        public TextFieldCell(JTable cellTable) {
            super();                            // calling parent constructor
            final JTable table = cellTable;     // this one is required to get cell editor and stop editing
    
            this.addFocusListener(new FocusListener() {
                public void focusGained(FocusEvent e) {
                }
    
                // this function successfully provides cell editing stop
                // on cell losts focus (but another cell doesn't gain focus)
                public void focusLost(FocusEvent e) {
                    CellEditor cellEditor = table.getCellEditor();
                    if (cellEditor != null)
                        if (cellEditor.getCellEditorValue() != null)
                            cellEditor.stopCellEditing();
                        else
                            cellEditor.cancelCellEditing();
                }
            });
        }
    }
    

    Default cell editor class:

    class TextFieldCellEditor extends DefaultCellEditor {
    TextFieldCell textField;    // an instance of edit field
    Class<?> columnClass;       // specifies cell type class
    Object valueObject;         // for storing correct value before editing
    public TextFieldCellEditor(TextFieldCell tf, Class<?> cc) {
        super(tf);
        textField = tf;
        columnClass = cc;
        valueObject = null;
    }
    
    @Override
    public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
        TextFieldCell tf = (TextFieldCell)super.getTableCellEditorComponent(table, value, isSelected, row, column);
        if (value != null) {
            tf.setText(value.toString());
        }
        // we have to save current value to restore it on another cell selection
        // if edited value couldn't be parsed to this cell's type
        valueObject = value;
        return tf;
    }
    
    @Override
    public Object getCellEditorValue() {
        try {
            // converting edited value to specified cell's type
            if (columnClass.equals(Double.class))
                return Double.parseDouble(textField.getText());
            else if (columnClass.equals(Float.class))
                return Float.parseFloat(textField.getText());
            else if (columnClass.equals(Integer.class))
                return Integer.parseInt(textField.getText());
            else if (columnClass.equals(Byte.class))
                return Byte.parseByte(textField.getText());
            else if (columnClass.equals(String.class))
                return textField.getText();
        }
        catch (NumberFormatException ex) {
    
        }
    
        // this handles restoring cell's value on jumping to another cell
        if (valueObject != null) {
            if (valueObject instanceof Double)
                return ((Double)valueObject).doubleValue();
            else if (valueObject instanceof Float)
                return ((Float)valueObject).floatValue();
            else if (valueObject instanceof Integer)
                return ((Integer)valueObject).intValue();
            else if (valueObject instanceof Byte)
                return ((Byte)valueObject).byteValue();
            else if (valueObject instanceof String)
                return (String)valueObject;
        }
    
        return null;
    }
    

    It the code of table initialization you have to add the following:

    myTable.setDefaultEditor(Float.class, new TextFieldCellEditor(new TextFieldCell(myTable), Float.class));
    myTable.setDefaultEditor(Double.class, new TextFieldCellEditor(new TextFieldCell(myTable), Double.class));
    myTable.setDefaultEditor(Integer.class, new TextFieldCellEditor(new TextFieldCell(myTable), Integer.class));
    

    Hope, this will help somebody who have the same problem.

    0 讨论(0)
  • 2020-12-01 08:32

    You need to add a focus listener. Given that JTable is basically a container of its cell components, you actually want the focus listener for every cell in your table that needs to behave in the way you indicated.

    To do this, you will need to create custom cell editor, which wraps the cell component that has a registered focus listener. And when you get the callback for the loss of focus event, you do the data save, as you require.

    This pretty much details most of what you need to do. The details of implementing the focus listener is not there, but that is fairly straightforward.

    Lets say you do use a JTextComponent as your cell component. Then:

    public void focusLost(FocusEvent e) {
       JTextComponent cell = (JTextComponent) e.getSource();  
       String data = cell.getText();
    
       // TODO: save the data for this cell
    }
    

    [p.s. edit]:

    The thread that is calling you with this event is the dispatch thread. Do NOT use it for actions with high latency. But if you are just flipping bits in the heap, it should be ok.

    0 讨论(0)
  • 2020-12-01 08:40

    Table Stop Editing explains whats happening and gives a couple simple solutions.

    0 讨论(0)
提交回复
热议问题