使用javafx创建自定义树

问题描述 投票:3回答:2

[基本上,我想知道是否可以创建树并在javaFX上对其进行自定义...我尝试这样做,但是到目前为止,使用此代码无法执行任何操作...

public class Main{
    ......

 public Main() throws Exception{
    ......    

   // TreeView created
    TreeView tv = (TreeView) fxmlLoader.getNamespace().get("treeview");

    TreeItem<String> rootItem = new TreeItem<String>("liss");
    rootItem.setExpanded(true);
    tv.setRoot(rootItem);

    /*for (int i = 1; i < 6; i++) {
        TreeItem<String> item = new TreeItem<String> ("Message" + i);
        rootItem.getChildren().add(item);
    }
    TreeItem<String> item = new TreeItem<String> ("MessageWoot");
    rootItem.getChildren().add(item);
*/
    //tv.setEditable(true);

    tv.setCellFactory(new Callback<TreeView<String>, TreeCell<String>>() {
        @Override
        public TreeCell<String> call(TreeView<String> arg0) {
            // custom tree cell that defines a context menu for the root tree item
            return new MyTreeCell();
        }
    });

    stage.show();
}

//
private static class MyTreeCell extends TextFieldTreeCell<String> {
    private ContextMenu addMenu = new ContextMenu();
    public boolean clickedFirstTime = false;

    public MyTreeCell() {
        // instantiate the root context menu
        MenuItem addMenuItem = new MenuItem("Expand");
        addMenu.getItems().add(addMenuItem);
        addMenuItem.setOnAction(new EventHandler() {

            public void handle(Event t) {
                TreeItem n0 =
                        new TreeItem<String>("'program'");
                TreeItem n1 =
                        new TreeItem<String>("<identifier>");
                TreeItem n2 =
                        new TreeItem<String>("body");

                getTreeItem().getChildren().add(n0);
                getTreeItem().getChildren().add(n1);
                getTreeItem().getChildren().add(n2);

            }
        });
    }

    @Override
    public void updateItem(String item, boolean empty) {
        super.updateItem(item, empty);

        // if the item is not empty and is a root...
        //if (!empty && getTreeItem().getParent() == null && this.clickedFirstTime) {
        System.out.println("UPDATEITEM -> clickedFirstTime : "+this.clickedFirstTime);
        if (!this.clickedFirstTime) {
            System.out.println("WOOT");
            setContextMenu(addMenu);
            this.clickedFirstTime = true;
        }
    }

}

而且我在问自己这是否是正确的“技术”,它将解决我正在尝试做的事情...

我的目标是什么?

首先,我正在寻找添加或删除treeItem。我必须说,某个treeItem只能添加一次或任意N次,例如,限制(例如:treeItem <6对于特定级别的作用域和树视图根的特定路径)。

第二,使某些treeItem可编辑,而另一些则不可编辑!当它为“可编辑”时,您可以为用户弹出一些内容以例如插入一些输入!

有可能吗?

我看过https://docs.oracle.com/javafx/2/ui_controls/tree-view.htm#BABJGGGF中的教程,但是我对此教程感到非常困惑...我不太了解单元工厂机制...当我只想要某个TreeItem时,他确实适用于TreeView的事实...或者我该如何控制这种效果/行为?我的意思是,我真的很迷上TreeView。也许,TreeView不是我想要的...

P.S .:我知道我无法应用任何视觉效果或向树项添加菜单,并且我使用单元工厂机制来克服此障碍。只是我不明白这个主意,我该怎么做!

java javafx treeview
2个回答
4
投票

如果要使用JavaFX,请确保这是正确的“技术”。但是,您可能应该对TreeItem使用更复杂的类型参数。您可以使用自定义TreeCell允许所需的用户交互。

此示例允许通过上下文菜单添加子节点和删除节点(除非内容为"nocontext")以及编辑内容(只要内容不是"noedit");在根节点上,delete选项被禁用:

    tv.setEditable(true);

    tv.setCellFactory(new Callback<TreeView<String>, TreeCell<String>>() {

        private final MyContextMenu contextMenu = new MyContextMenu();
        private final StringConverter converter = new DefaultStringConverter();

        @Override
        public TreeCell<String> call(TreeView<String> param) {
            return new CustomTreeCell(contextMenu, converter);
        }

    });
public class CustomTreeCell extends TextFieldTreeCell<String> {

    private final MyContextMenu contextMenu;

    public CustomTreeCell(MyContextMenu contextMenu, StringConverter<String> converter) {
        super(converter);
        if (contextMenu == null) {
            throw new NullPointerException();
        }
        this.contextMenu = contextMenu;
        this.setOnContextMenuRequested(evt -> {
            prepareContextMenu(getTreeItem());
            evt.consume();
        });
    }

    private void prepareContextMenu(TreeItem<String> item) {
        MenuItem delete = contextMenu.getDelete();
        boolean root = item.getParent() == null;
        if (!root) {
            delete.setOnAction(evt -> {
                item.getParent().getChildren().remove(item);
                contextMenu.freeActionListeners();
            });
        }
        delete.setDisable(root);
        contextMenu.getAdd().setOnAction(evt -> {
            item.getChildren().add(new TreeItem<>("new item"));
            contextMenu.freeActionListeners();
        });
    }

    @Override
    public void updateItem(String item, boolean empty) {
        super.updateItem(item, empty);
        if (!empty) {
            setContextMenu("nocontext".equals(item) ? null : contextMenu.getContextMenu());
            setEditable(!"noedit".equals(item));
        }
    }

}
public class MyContextMenu {
    private final ContextMenu contextMenu;
    private final MenuItem add;
    private final MenuItem delete;

    public MyContextMenu() {
        this.add = new MenuItem("add child");
        this.delete = new MenuItem("delete");
        this.contextMenu = new ContextMenu(add, delete);
    }

    public ContextMenu getContextMenu() {
        return contextMenu;
    }

    public MenuItem getAdd() {
        return add;
    }

    public MenuItem getDelete() {
        return delete;
    }

    /**
     * This method prevents memory leak by setting all actionListeners to null.
     */
    public void freeActionListeners() {
        this.add.setOnAction(null);
        this.delete.setOnAction(null);
    }

}

当然可以在updateItemprepareContextMenu中进行更复杂的检查,并且可以支持不同的用户交互(TextFieldTreeCell可能不是适合您的超类;您可以使用“普通” TreeCell和当用户在上下文菜单中选择MenuItem时,显示另一个阶段/对话框来编辑项目。

关于细胞工厂的一些说明

单元格工厂用于在显示数据的类中创建单元格(例如TableColumnTreeViewListView)。当此类需要显示内容时,它将使用其单元工厂来创建用于显示数据的Cell。在这样的单元格中显示的内容可能会更改(请参见updateItem方法)。

示例

(我不是100%肯定这是正确的方法,但是应该足够接近)

创建一个updateItem以显示具有2个非扩展子级的扩展根节点。

TreeView确定需要为根节点显示3个项目,它是2个子节点。因此,TreeView使用它的单元格工厂来创建3个单元格,并将它们添加到其布局中并分配显示的项目。

现在,用户展开第一个孩子,该孩子拥有两个孩子。 TreeView确定还需要2个单元格才能显示项目。为了提高效率,在布局的末尾添加了新的单元格,并更新了单元格的项目:

  • 以前包含最后一个孩子的单元格已更新,现在包含第一项的第一个孩子。
  • 2个新添加的单元格被更新为分别包含第一个孩子的第二个孩子和根孩子的第二个孩子。

0
投票

因此,我决定取消TreeView(因为文档太垃圾了……),而是决定实现一个Webview!

为什么?

类似,我可以创建一个HTML文档并在其中使用jstree(jquery插件-TreeView)。这是一个将基本创建树视图的插件。

不幸的是,该文档比treeview oracle文档好十倍。

而且,使用jstree创建/编辑树的可能性更好。得出的结论是,这是我可以为我找到的最佳解决方案。

[此外,无论谁愿意读我,我都与webview和javafx应用程序架起了桥梁!它是我的HTML文档和Java应用程序之间的连接(在此处了解更多信息-https://www.jstree.com)。

希望它将帮助更多的人。

© www.soinside.com 2019 - 2024. All rights reserved.