Creating a custom tree with javafx

Dam*_*iii 3 java treeview javafx

Basically, I wanted to know if I could create a tree and custom it on javaFX... I tried to do it, but couldn't do anything so far with this code...

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;
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

And I'm questioning myself if this is the right "technology" which will solve what I'm trying to do...

What's my objective in this?

Firstly, I'm looking to add or delete a treeItem. I must say that a certain treeItem may be added only once or any N times, like a restriction (for example: treeItem < 6 for a certain level scope and a certain path of the root of tree view).

Secondly, make some treeItem editable and others not editable! When it is Editable, you may pop up something for the user in order to insert some input for example!

Is it possible ?

I saw the tutorial from https://docs.oracle.com/javafx/2/ui_controls/tree-view.htm#BABJGGGF but I'm really confused with this tutorial ... I don't really understand the cell factory mechanism... The fact that he does apply to TreeView when i want only a certain TreeItem... Or how could I control that effect/behaviour ? I mean, I'm really really lost with TreeView. Probably, TreeView isn't what I'm looking for ...

P.S.: I know that I cannot apply any visual effect or add menus to a tree items and that i use a cell factory mechanism to overcome this obstacle. Just I don't understand the idea and how could I do it !

fab*_*ian 5

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

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

    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);
        }
    
    });
Run Code Online (Sandbox Code Playgroud)
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));
        }
    }

}
Run Code Online (Sandbox Code Playgroud)
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);
    }
    
}
Run Code Online (Sandbox Code Playgroud)

当然,可以在updateItem和 中完成更复杂的检查,prepareContextMenu并且可以支持不同的用户交互(TextFieldTreeCell可能不是适合您的超类;您可以使用“正常”TreeCell并在用户选择时显示不同的阶段/对话框来编辑项目aMenuItem在上下文菜单中)。

关于细胞工厂的一些说明

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

例子

(我不是 100% 确定这正是它完成的方式,但它应该足够接近)

TreeView创建A以显示具有 2 个非扩展子节点的扩展根节点。

所述TreeView确定其需要显示3项的根节点和它的2个孩子。该TreeView所以采用它的电池工厂,以创建3个细胞,并将它们添加到它的布局和分配显示的项目。

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

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