5

我正在尝试使用 treeView 创建菜单。这是我第一次使用 treeView 并且一直在几个网站上阅读它。

当涉及到动作事件时,我遇到了一些问题。我想要做的基本上是在用户单击树视图中的节点时触发和事件到目前为止,我有以下内容:

        TreeItem<String> rootItem = new TreeItem<String>("Navigation");
    TreeItem<String> statistics = new TreeItem<String>("Statistics");
    TreeItem<String> clan = new TreeItem<String>("Clan page");
    clan.addEventHandler(MouseEvent, new EventHandler<MouseEvent>() {

        @Override
        public void handle(MouseEvent event) {
            // TODO Auto-generated method stub

        }
    });

    rootItem.getChildren().add(statistics);
    rootItem.getChildren().add(clan);

    TreeView<String> tree = new TreeView<String>(rootItem); 

可悲的是,这似乎不起作用。

有什么方法可以在不更改to 类型的情况下将 aclicklistener甚至 an添加actionlistener到 my 中的各个项目?treeViewtreeItemsButton

4

5 回答 5

15

这可以通过实现 CellFactory 来解决,但我认为最简单的方法是这样的:

1) 创建并向 TreeView 添加事件处理程序:

EventHandler<MouseEvent> mouseEventHandle = (MouseEvent event) -> {
    handleMouseClicked(event);
};

treeView.addEventHandler(MouseEvent.MOUSE_CLICKED, mouseEventHandle); 

2)仅处理节点上的点击(而不是 TreeView 的空白空间):

private void handleMouseClicked(MouseEvent event) {
    Node node = event.getPickResult().getIntersectedNode();
    // Accept clicks only on node cells, and not on empty spaces of the TreeView
    if (node instanceof Text || (node instanceof TreeCell && ((TreeCell) node).getText() != null)) {
        String name = (String) ((TreeItem)treeView.getSelectionModel().getSelectedItem()).getValue();
        System.out.println("Node click: " + name);
    }
}
于 2014-11-03T13:00:26.573 回答
9

我在鼠标事件中找不到方法 getPickResult,所以也许 next 比 Alex 的回答更可取:

1)将侦听器添加到树视图

treeView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> handle(newValue));

2) 处理点击,不需要区分对空白和节点的点击

private void updateSelectedItem(Object newValue) {      
    System.out.println(newValue);
}
于 2015-07-05T15:10:26.337 回答
5

根据JavaFX 2.2 文档

“ ..a TreeItem不是Node ,因此不会在TreeItem 上触发视觉事件,要获取这些事件,有必要将相关观察者添加到 TreeCell 实例(通过自定义单元工厂)。”

我认为这个使用示例TreeView会有所帮助。

于 2013-04-03T19:35:01.577 回答
0
treeView.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<TreeItem<String>>() {

                @Override
                public void changed(ObservableValue<? extends TreeItem<String>> observable,
                        TreeItem<String> oldValue, TreeItem<String> newValue) {
                    // newValue represents the selected itemTree
                }

            });
于 2020-06-15T07:59:46.913 回答
0

我实际上以一种迂回的方式完成了这一点,尽管它非常灵活,甚至允许您拥有与不同类相关的 TreeItems,同时仍然能够将它们嵌套在彼此中。

最初的检查可能会让人望而生畏,但如果你密切关注它,那将是完全合理的。

您从包含 TreeView 的类开始 - 我们将其称为 TreeViewClass。

这个例子来自我正在开发的一个与 GitHub Gists 交互的程序,它解释了我在类中与之交互的各种对象。

public class TreeViewClass {

    public void start() {
        TextArea itemInfo = new TextArea();
        SplitPane splitPane = new SplitPane(getTreeView(), itemInfo);
        splitPane.setDividerPosition(0, .1);
        Stage stage = new Stage();
        stage.setScene(new Scene(splitPane));
        stage.show();
    }

    private Collection<TreeItem<MyTreeItem>> gistObjectTreeItems() {
        Collection<TreeItem<MyTreeItem>> gistTreeItemObjects = FXCollections.observableArrayList();
        List<GistObject> gistObjects = GitHubApi.getGistObjectList();

        for (GistObject gistObject : gistObjects) {
            MyTreeItem myGistObjectTreeItem = new MyTreeItem(gistObject);
            TreeItem<MyTreeItem> objectTreeItem = new TreeItem<>(myGistObjectTreeItem);

            List<GistFile> gistFileList = gistObject.getFileList();
            for(GistFile file : gistFileList) {
                TreeItem<MyTreeItem> fileTreeItem = new TreeItem<>(new MyTreeItem(file));
                objectTreeItem.getChildren().add(fileTreeItem);
            }
            gistTreeItemObjects.add(gistObjectTreeItem);
        }
        return gistTreeItemObjects;
    } 
/**
We are returning a Collection of TreeItems, each one contains an instance
of MyTreeItem using the constructor that defines the instance as a GistObject.
We then add to each of those objects, more TreeItems that contain the same
class MyTreeItem, only we use the constructor that sets the instance as
GistFile, and obviously, each Gist can have many GistFiles.
**/
    
    private TreeView<MyTreeItem> getTreeView() {
        TreeView<MyTreeItem> treeView = new TreeObject(this);
        TreeItem<MyTreeItem> treeRoot = new TreeItem<>(new MyTreeItem());
        treeRoot.getChildren().addAll(gistObjectTreeItems());
        treeView.setRoot(treeRoot);
        treeView.setShowRoot(false);
        return treeView;
    }
/**
getTreeView merely builds the TreeView, then adds to it, the Collection
of MyTreeItems that contain the GistObject and each of those objects
GistFiles. We need a root in the tree, so we add one that also contains
the MyTreeItem class only we use the default constructor, since it is
neither a GistObject or a GistFile, and then we hide it because we
don't need to interact with it. 
**/
}

这是主要的 TreeView 扩展类:

import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.input.MouseEvent;

public class TreeObject extends TreeView<MyTreeItem> {

    public TreeObject() {
        super();
        addEventHandler(MouseEvent.MOUSE_CLICKED, e->{
            if(e.getClickCount() == 1) {
                selected = getSelectionModel().getSelectedItem();
                if (selected != null) {
                    gistFile = selected.getValue().getFile();
                    gist     = selected.getValue().getGist();
                    if (gistFile != null) {
                        GitHubApi.setSelected(gistFile);
                    }
                    else if (gist != null) {
                        GitHubApi.setSelected(gist);
                    }
                }
            }
        });
    }

    TreeItem<MyTreeItem> selected;
    GistFile             gistFile;
    GistObject           gist;
}
    
/**
This is the class that gives us the ability to add all manner of events that we might
want to look for. In this case, I am looking for MOUSE_CLICKED events and notice I can
even check to see how many mouse clicks the user enacted on the TreeItems. When the user
clicks on the TreeItem, the selected object returns the MyTreeItem instance that
is assigned to whichever TreeItem they click on, I can then test to see which way 
MyTreeItem has been configured (GistObject or GistFile) and act accordingly.
**/

最后,我们有 MyTreeItem 类:

public class MyTreeItem {

    private enum TYPE {
        GIST, FILE
    }

    public MyTreeItem(GistObject gist) {
        this.gist = gist;
        this.type = TYPE.GIST;
    }

    public MyTreeItem(GistFile file) {
        this.file = file;
        this.type = TYPE.FILE;

    }

    public MyTreeItem(){}

    private TYPE type;
    private GistObject gist;
    private GistFile   file;

    public boolean isGist() { return type == TYPE.GIST; }

    public boolean isFile() { return type == TYPE.FILE; }

    public GistObject getGist() { return gist; }

    public GistFile getFile() { return file; }

    @Override
    public String toString() {
        if(this.type == TYPE.FILE) return StringUtils.truncate(file.toString(),25);
        else if(this.type == TYPE.GIST) return StringUtils.truncate(gist.toString(),25);
        return "";
    }
}
/**
This should be fairly self-explanatory. This is the MyTreeItem class that can be instantiated
as either a GistObject or a GistFile. Note the toString returns different Strings depending
on how the class was instantiated. The toString() will automatically populate the label
on the TreeItem object that the class is assigned to.
**/
于 2021-11-04T09:03:41.127 回答