禁用TreeItem双击时默认的展开/折叠功能(JavaFX 8)

5
如何在TreeView中双击TreeItem时禁用默认的展开/折叠行为?
我的问题与此问题完全相同:Disable TreeItem's default expand/collapse on double click JavaFX 2.2。不幸的是,提供的答案不再有效(如果它们曾经有效)。
更新1: 很遗憾,我不知道重要的是要提到我不使用标准的TreeItem,而是一个修改过的版本。它的构造函数如下:
public BasicTreeItem(String text) {
  super();

  _label = new Label(text);
  stateIndicator = new ImageView();
  this.setToNever(); // sets the stateIndicator mentioned above

  _hb = new HBox();
  this.setValue(_hb);
  _hb.getChildren().add(stateIndicator);
  _hb.getChildren().add(_label);
  _hb.setAlignment(Pos.CENTER_LEFT);
}

因此,在最终看到的东西是 HBox。

好奇:为什么你想要改变标准(至少在win上)的用户体验?典型的用户对这样的调整并不满意 ;) - kleopatra
我的TreeItems包含一个特殊的CheckBox,有4种不同的状态,我想快速更改它们。如果我在它们上面点击得太快,TreeItems会展开/折叠,这是我不想要的。但是如果我双击标签,我仍然希望该项展开/折叠。 - yoshegg
谢谢提供信息 :) - kleopatra
1个回答

7
你可以使用 TreeViewCellFactory,将一个 EventFilter 附加到树的 TreeCell 上的鼠标监听器。 示例:
public class TreeViewExample extends Application {

    @Override
    public void start(Stage primaryStage) {
        StackPane root = new StackPane();

        TreeView tw = new TreeView();
        TreeItem rootNode = new TreeItem("Root");
        TreeItem blockOne = new TreeItem("Block1");
        TreeItem childA = new TreeItem("ChildA");
        TreeItem childB = new TreeItem("ChildB");
        blockOne.getChildren().add(childA);
        blockOne.getChildren().add(childB);
        TreeItem blockTwo = new TreeItem("Block2");
        TreeItem childC = new TreeItem("ChildC");
        TreeItem childD = new TreeItem("ChildD");
        blockTwo.getChildren().add(childC);
        blockTwo.getChildren().add(childD);
        rootNode.getChildren().add(blockOne);
        rootNode.getChildren().add(blockTwo);
        tw.setRoot(rootNode);


        tw.setCellFactory(param -> {
            TreeCell<String> treeCell = new TreeCell<String>() {
                @Override
                protected void updateItem(String item, boolean empty) {
                    super.updateItem(item, empty);
                    if (item == null || empty) {
                        setText("");
                        setGraphic(null);
                        return;
                    }

                    setText(item.toString());
                }
            };

            treeCell.addEventFilter(MouseEvent.MOUSE_PRESSED, (MouseEvent e) -> {
                if (e.getClickCount() % 2 == 0 && e.getButton().equals(MouseButton.PRIMARY))
                    e.consume();
            });
            return treeCell;
        });

        root.getChildren().add(tw);
        Scene scene = new Scene(root, 300, 250);
        primaryStage.setTitle("TreeView!");
        primaryStage.setScene(scene);
        primaryStage.show();
    }
}

针对您的特定用例的更新:

首先,您的TreeItem实现非常奇怪,它必须像这样:

class BasicTreeItem extends TreeItem<HBox>

使用GUI元素作为模型是一种不好的做法。你可以实现一个类来存储每个项目可能具有的所有状态,并将该类用作TreeView的模型,也可以将其用作BasicTreeItem实现的通用参数。
但是回答更新问题:如果你想在双击Label时展开,但如果双击ImageView则不想展开。
在这种情况下,你可以完全删除CellFactory并将EventFilter添加到ImageView(例如,在BasicTreeItem构造函数中)。
stateIndicator.addEventFilter(MouseEvent.MOUSE_PRESSED, (MouseEvent e) -> {
    if (e.getClickCount() % 2 == 0 && e.getButton().equals(MouseButton.PRIMARY))
        e.consume();
});

使用该过滤器后,您的 TreeItem 将具有标准的双击行为,除了在 ImageView 上双击。


非常感谢。不幸的是,我忘了提到我使用自己的TreeItem版本,现在已经添加到原始问题中。如果您知道如何为其设置setCellFactory,我将非常高兴 :) - yoshegg
做到了:)。在我的下一个项目中,我肯定不会再将GUI元素用作模型,但现在我已经走得太远,无法重新设计一切。 - yoshegg
只是为了明确,你所说的“使用GUI元素作为模型是一种不好的做法”是什么意思?这是因为使用了HBox(实际上只是用来在TreeView中显示相关信息)还是因为我在TreeItem中存储数据?基本上,我想知道TreeItem是否可以作为一个模型来存储数据(通过扩展TreeItem类),甚至可以使用更多的TreeItem来存储树状结构的数据。 - yoshegg
谢谢!我一直在想为什么我的尝试没有奏效。我过滤的是 MOUSE_CLICKED 事件而不是 MOUSE_PRESSED。 - swpalmer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接