Может ли Jtable сохранять данные всякий раз, когда ячейка теряет фокус?

Высокий уровень: у меня есть JTable, который пользователь может использовать для редактирования данных.

Всякий раз, когда пользователь нажимает Enter или Tab для завершения редактирования, данные сохраняются (я предполагаю, что «сохраненные» действительно означают «вызывается метод setValueAt() TableModel».)

Если пользователь покидает ячейку каким-либо другим способом после редактирования, новые данные не сохраняются, и значение остается таким, каким оно было. Так, например, если пользователь изменяет значение, а затем щелкает какой-либо другой виджет на экране, изменение не «прилипает».

Я считаю, что это поведение по умолчанию для JTable, заполненного строками, да?

По разным причинам желательно, чтобы ячейка сохраняла все изменения всякий раз, когда пользователь покидает ячейку. Какой лучший/правильный способ заставить Swing сделать это?


person Electrons_Ahoy    schedule 31.10.2009    source источник


Ответы (3)


Редактирование остановки таблицы объясняет, что происходит, и предлагает несколько простых решений. .

person camickr    schedule 31.10.2009

Одно из предложенных простых решений

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

хорош только для строковых столбцов. Проблема в том, что если у меня, например, редактируемый столбец типа Float, я ввожу пустую строку в соответствующую ячейку и затем нажимаю на любой другой элемент управления окна — Java бросает NullPointerException в CellEditorRemover.propertyChange() метод JTable.java. Он использует вызов getCellEditor() для остановки или отмены редактирования, но в этом случае возвращает null. Если введенное значение не пусто или если я удалю флаг terminateEditOnFocusLost, все в порядке. Вероятно, описанная ситуация является багом.

Я надеюсь, что смогу предоставить решение, основанное на одном из предыдущих сообщений. Это не так тривиально, как я предполагал раньше, но мне кажется, что это работает. Мне пришлось унаследовать свой собственный редактор ячеек от редактора ячеек по умолчанию и собственное текстовое поле от JTextField, у которого есть FocusListener. Этот прослушиватель фокуса отлично работает, когда редактируемая ячейка теряет фокус, а фокус получает другой элемент управления окна. Но в случае изменения фокуса выбора ячейки слушатель «глух». Вот почему я также должен помнить ранее действительное значение перед началом редактирования, чтобы восстановить его, если введенное значение будет недействительным.

См. код ниже. Проверено с Double, Float и Integer, но я надеюсь, что это также будет работать с Byte и String.

Текстовое поле с прослушивателем фокуса:

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();
            }
        });
    }
}

Класс редактора ячеек по умолчанию:

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;
}

В код инициализации таблицы необходимо добавить следующее:

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));

Надеюсь, это поможет кому-то, у кого такая же проблема.

person ezze    schedule 13.11.2010

Вам нужно добавить прослушиватель фокуса. Учитывая, что JTable в основном является контейнером своих компонентов ячеек, вам действительно нужен прослушиватель фокуса для каждой ячейки в вашей таблице, которая должна вести себя так, как вы указали.

Для этого вам нужно будет создать пользовательский редактор ячеек, который обертывает компонент ячейки, имеющий зарегистрированный прослушиватель фокуса. И когда вы получаете обратный вызов для события потери фокуса, вы сохраняете данные, как вам нужно.

Это в значительной степени детализирует большую часть того, что вам нужно сделать. Деталей реализации прослушивателя фокуса там нет, но это довольно просто.

Допустим, вы используете JTextComponent в качестве компонента ячейки. Потом:

public void focusLost(FocusEvent e) {
   JTextComponent cell = (JTextComponent) e.getSource();  
   String data = cell.getText();

   // TODO: save the data for this cell
}

[пс. редактировать]:

Поток, который вызывает вас с этим событием, является потоком отправки. НЕ используйте его для действий с высокой задержкой. Но если вы просто переворачиваете биты в куче, все должно быть в порядке.

person alphazero    schedule 31.10.2009
comment
Урок, который следует усвоить человечеству в контексте коллективных знаний, хранящихся на эфемерных носителях. - person alphazero; 28.12.2012
comment
У Wayback Machine это было — я соответственно изменил ссылку. - person Peter Becker; 29.10.2013