以编程方式编辑 TreeView/TreeItem

Programmatically edit TreeView/TreeItem

编辑 #2:由于它看起来像一个错误,我已经 post 在 javaFx-jira 中编辑了错误报告。您必须拥有一个帐户才能访问该问题。如果有新信息,我会及时更新 post。

原文post: 我有一个带有按钮和 TreeView 的简单 UI。如果按钮被按下,应该有一个新项目添加到 TreeView。该项目一出现在树中就应该可以编辑。

import javafx.fxml.FXML;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.control.cell.TextFieldTreeCell;
import javafx.util.converter.DefaultStringConverter;

public class ClientController {

    @FXML
    private TreeView<String> tree;

    public void initialize() {
        tree.setEditable(true);
        tree.setCellFactory(p -> new TextFieldTreeCell<>(new DefaultStringConverter()));

        TreeItem<String> root = new TreeItem<>();
        root.setValue("Items");
        root.setExpanded(true);

        tree.setRoot(root);
    }

    @FXML
    public void createItem() {
        TreeItem<String> newItem = new TreeItem<>();
        newItem.setValue("Item " + tree.getExpandedItemCount());

        tree.getRoot().getChildren().add(newItem);
        tree.requestFocus();
        tree.getSelectionModel().select(newItem);
        tree.edit(newItem);
    }
}

我使用的 CellFactory 是 part of the JavaFX api.

如果我查看 api 文档 (TreeView#edit),我的网站上就没什么可做的了。

我通过 google 找到的许多示例,例如 this 为每个 TreeItem 创建了一个上下文菜单。有用,但不是我现在想要的。

如果我 select/double-click UI 中的项目,我可以编辑任何以前创建的和现有的 TreeItem。我错过了什么吗?

编辑#1:

如果 createItem 方法更改为以下代码:

@FXML
public void createItem() throws InterruptedException {
    TreeItem<String> newItem = new TreeItem<>();
    newItem.setValue("Item " + this.tree.getExpandedItemCount());

    this.tree.getRoot().getChildren().add(newItem);
    this.tree.requestFocus();
    this.tree.getSelectionModel().select(newItem);
    Thread.sleep(100);
    Platform.runLater(new Runnable() {
        @Override
        public void run() {
            SimpleController.this.tree.edit(newItem);
        }
    });
}

每个新项目都正确标记为编辑(已选择创建的文本字段及其内容)。 在没有 Thread.sleep 的情况下使用 Platform.runLater 不起作用,在没有 runLater[=50 的情况下使用 Thread.sleep =] 都没有。

这简直感觉不对。我的问题是什么?

我提供了一个非常小的示例(eclipse)项目: https://www.dropbox.com/s/duos0ynw4rqp3yn/Test.zip?dl=0 包含主要方法、FXML 文件和 "problematic" 控制器。

看来,在新添加的项目反映到它之前,treeView 需要更多时间来进行内部计算。要准确确定是哪些计算导致了问题,需要挖掘源代码并查看幕后发生的事情。

解决方法是强制 treeView 立即进行计算,

@FXML
public void createItem() throws InterruptedException {
    TreeItem<String> newItem = new TreeItem<>();
    newItem.setValue("Item " + tree.getExpandedItemCount());

    tree.getRoot().getChildren().add(newItem);

    // Trigger whatever calculations there internally
    TreeItem<String> r = tree.getRoot();
    tree.setRoot( null );
    tree.setRoot( r );

    tree.requestFocus();
    tree.getSelectionModel().select(newItem);
    tree.edit(newItem);
}

根据 JavaFX 中的其他错误完全凭直觉找到了此解决方法;-)


或者,由于Platform.runlaters推迟的时间明显不够,可以用PauseTransition增加所需时间:

PauseTransition p = new PauseTransition( Duration.millis( 100 ) );
p.setOnFinished( new EventHandler<ActionEvent>()
{
    @Override
    public void handle( ActionEvent event )
    {
        tree.edit( newItem );
    }
} );
p.play();

TreeCells(即它们的内容和与 treeItem 的绑定)在布局过程中延迟更新,当下一个脉冲被触发时非常 "late"。 Platform.runLater(..) 或任何硬编码延迟可能发生在该脉冲之前或之后,这(可能)就是为什么两者似乎都有效或无效的原因。

所以另一个古怪的技巧是在添加新项目之后和以编程方式开始编辑之前手动强制在树上重新布局:

root.getChildren().add(newItem);
tree.layout();
tree.edit(newItem);

不用说,这不是必需的 - 当前的行为是一个严重的错误,必须立即修复(...意思是...jdk 9)