【问题标题】:JavaFx TableView - saving edits on focus lost doesn't work if focus is given to same columnJavaFx TableView - 如果将焦点赋予同一列,则保存失去焦点的编辑不起作用
【发布时间】:2016-10-06 06:13:21
【问题描述】:

我一直在努力解决这个问题,似乎找不到任何逻辑解决方法。

当用户点击离开时,我正在尝试保存表格单元格的内容。在关注this tutorial 之后,我有表格单元格,当用户单击同一列时,它们会在失去焦点时保存他们的编辑除了

我知道为什么会出现这个问题,如果isEditing() 为真,commitEdit() 方法将立即返回(当您单击同一列时也是如此。

 public void commitEdit(T newValue) {
        if (! isEditing()) return;
    ...

我尝试覆盖该方法无济于事。我可以强制更新单元格内容,但我不知道如何在不知道我所在的单元格的情况下强制编辑单元格值。

如果有办法让我获取我所在单元格的字段名称,我可以使用反射来强制更新,但我不知道如何获取字段名称,甚至不知道是否有可能。

【问题讨论】:

  • 您链接的教程已经过时了。请参阅here 的讨论(不知道您所说的“不知道我在哪个牢房”是什么意思...)
  • 我用我自己的 hack 对一个包装器进行了反射来提交。立即发布答案
  • @James_D 我刚刚发布了我的答案,是否有任何 cmets、疑虑、建议以使其变得更好? (现在可以使用)
  • 有一堆与 commit-on-focus-lost 相关的错误(在假期中,所以没有我的知识库方便,抱歉 - 但你可能会搜索一些 QAs),他们会不能在 jdk9 中修复。因此,任何特定于上下文的 hack 都比 core 更好......要高兴并记住它可能会在其他任何地方失败
  • @kleopatra 是的,这就是我所听到的。很遗憾,这是一个重大错误。我已经在 2 天的大部分时间里一直在解决这个问题,所以我已经阅读了大部分 SO 线程,并看到了许多其他解决它的技巧。但是我无法修复我遇到的错误。

标签: java javafx tableview


【解决方案1】:

经过挖掘,我发现了如何获取列的属性名称。有了这个,我继续写了一些通用的反射来强制更新。为了便于使用,我将所有内容都包装在 commit(Object val) 方法中。这些是对使用hereEditCell 类的修改。

免责声明:这仅在您使用 PropertyValueFactory 并遵循行类中的命名约定时才有效。这也是非常善变的代码,使用和修改请自行斟酌。

我将单元格修改为带有public static class EditingCell<S, T> extends TableCell<S, T> 的通用单元格。教程中的其他所有内容都应该保持不变,如果不能随时告诉我,我会在此处进行相应更新。

public void commit(Object val) {

        // Get the table
        TableView<S> t = this.getTableView();

        // Get the selected row/column
        S selectedRow = t.getItems().get(this.getTableRow().getIndex());
        TableColumn<S, ?> selectedColumn = t.getColumns().get(t.getColumns().indexOf(this.getTableColumn()));

        // Get current property name
        String propertyName = ((PropertyValueFactory) selectedColumn.getCellValueFactory()).getProperty();

        // Create a method name conforming to java standards ( setProperty )
        propertyName = ("" + propertyName.charAt(0)).toUpperCase() + propertyName.substring(1);

        // Try to run the update
        try {

            // Type specific checks - could be done inside each setProperty() method
            if(val instanceof Double) {
                Method method = selectedRow.getClass().getMethod("set" + propertyName, double.class);
                method.invoke(selectedRow, (double) val);
            }
            if(val instanceof String) {
                Method method = selectedRow.getClass().getMethod("set" + propertyName, String.class);
                method.invoke(selectedRow, (String) val);
            }
            if(val instanceof Integer) {
                Method method = selectedRow.getClass().getMethod("set" + propertyName, int.class);
                method.invoke(selectedRow, (int) val);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        // CommitEdit for good luck
        commitEdit((T) val);
    }

然后由于文本字段不会更新,我在cancelEdit() 中强制对其进行更新。这有点特定于我的情况(我想要默认值 0.0,并且只接受双精度值) - 根据需要进行修改。

@Override
public void cancelEdit() {
    super.cancelEdit();

    // Default value
    String val = "0.0";

    // Check to see if there's a value
    if (!textField.getText().equals(""))
        val = textField.getText();

    // Set table cell text
    setText("" + val);
    setGraphic(null);
}

【讨论】:

  • 如果你的单元格值工厂不是PropertyValueFactory,那将非常可怕地失败,坦率地说,我无论如何都不会使用它。为什么不直接将 Consumer&lt;T&gt; 传递给您的自定义单元实现?
  • 我不确定Consumer&lt;T&gt; 是什么,因为不幸的是,我遵循了这两个帖子中链接的JavaFx 2 教程,这些帖子使用PropertyValueFactory 创建表列。我正在与之交互的这个表是在 fxml 中创建的
  • FXML 和它有什么关系? PropertyValueFactory 只是setCellValueFactory 所需的Callback 的一种方便实现,并不是一个很好的实现。我只是指标准 Java Consumer。您正在寻找的只是一种处理新值的方法,即只是一个将T 作为参数的函数。所以只需将其提供给单元格。
  • 我不完全确定,我一般是 JavaFx 的新手(PropertyValueFactory 来创建表。
  • 一般来说,回避通常的提交机制是一个非常糟糕的主意(通过在 commitEdit 中通过任何方式显式更新值。也就是说,有一堆与提交相关的错误 -失去焦点(在假期中,所以没有我的知识库,对不起 - 但你可能会搜索一些质量保证),它们不会在 jdk9 中修复。所以任何特定于上下文的 hack 都比核心更好......快乐并记住,它可能会在其他任何地方失败
【解决方案2】:

看来您正在寻找的只是细胞处理新(或旧)值并将它们写回模型的方法。为什么不直接以BiConsumer&lt;S,T&gt; 的形式提供回调?

public class EditingCell<S,T> extends TableCell<S,T> {

    private final BiConsumer<S,T> updater ;

    public EditingCell(BiConsumer<S,T> updater) {
        this.updater = updater ;
    }

    // ....

    // not really sure what this method is for:
    public void commit(T val) {
        S rowValue = getTableView().getItems().get(getIndex());
        updater.accept(rowValue, val);
    }

    // wouldn't this be better?
    @Override
    public void commitEdit(T newValue) {
        super.commitEdit(newValue);
        S rowValue = getTableView().getItems().get(getIndex());
        updater.accept(rowValue, val);
    }

    // ...
}

然后你会做这样的事情:

TableView<Person> table = new TableView<>();

TableColumn<Person, String> firstNameColumn = new TableColumn<>("First Name");
firstNameColumn.setCellValueFactory(cellData -> cellData.getValue().firstNameProperty());
firstNameColumn.setCellFactory(col -> new EditingCell(Person::setFirstName));

【讨论】:

  • 不要......除非是在破解错误时;-)
  • 就目前而言,由于我的 hack 工作正常,我将保持原样 - 谢谢,但如果我打破某些东西,我肯定会回头看看。
  • 嗯,是的,破解漏洞是我们非常明确的目的......
  • 我通常很擅长破解自己的目标,但这个可能是最奇怪和最令人困惑的破解错误。不过谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-19
  • 2011-11-11
  • 2013-03-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多