当前位置: 首页 > 知识库问答 >
问题:

用javafx创建自定义树

卫开济
2023-03-14

基本上,我想知道我是否可以创建一个树并在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;
        }
    }

}

我在质疑自己,这是否是正确的“技术”,可以解决我想做的事情...

我从https://docs.oracle.com/javafx/2/ui_controls/tree-view.htm#babjgggf看到了这个教程,但我对这个教程真的很困惑...我不太了解细胞工厂的机制...事实上,当我只想要某个树项时,他确实适用于树视图...或者我如何控制这种影响/行为?我是说,我真的真的很迷恋树景。也许TreeView不是我想要的...

附注:我知道我不能应用任何视觉效果或添加菜单到一个树项目,我使用一个细胞工厂机制来克服这个障碍。只是我不明白这个想法,我怎么能做到!

共有1个答案

田佐
2023-03-14

当然,如果您想使用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);
        }
    
    });
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)。当此类需要显示内容时,它使用它的单元格工厂创建用于显示数据的单元格。在这样的单元格中显示的内容可能会更改(请参见UpdateItem方法)。

(我不是百分之百确定这是正确的方式,但它应该足够接近)

创建TreeView以显示一个展开的根节点,该根节点有2个未展开的子节点。

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

现在用户展开第一个子项,它有自己的两个子项。TreeView确定还需要2个单元格来显示这些项。为了提高效率,新单元格被添加到布局的末尾,单元格的项被更新:

  • 以前包含最后一个子项的单元格被更新,现在包含第一项的第一个子项。
  • 新添加的2个单元格被更新为包含第一个子单元格的第二个子单元格和根的第二个子单元格。
 类似资料:
  • 问题内容: 我正在尝试在javaFX中创建自定义光标。这是我的代码: Windows 8.1的游标创建无效吗? 问题答案: 检出ImageCursor.getBestSize()方法和ImageCursor.getMaximumColors()并查看它们返回的内容,然后尝试匹配最佳大小和最大颜色的自定义光标图像。对于Windows 8.1,这很可能是32x32的光标。 这是来自javadoc 的引

  • 如有任何建议或帮助,将不胜感激。 曼迪

  • 问题内容: 注释如何与Java一起使用?以及如何创建这样的自定义注释: 基本上,我需要保留的POJO在持久化时像这样进行序列化: 这样,实际的生成/持久对象是这样的: 任何想法如何实现这一点? 问题答案: 如果创建自定义注释,则必须使用此处的 API 示例进行处理。您可以参考如何声明注释。 这是Java中的示例注释声明的样子。 并被称为。 表示您想在运行时保留注释,并且可以在运行时访问它。 表示您

  • 问题内容: 我在Django中启用了用户身份验证模块,但是当我使用它时,它仅询问用户名和两个密码/密码确认字段。我还希望将电子邮件和全名字段全部设置为必填字段。 我已经做到了: 现在,该表单显示了新字段,但是没有将它们保存到数据库中。 我怎样才能解决这个问题? 问题答案: 用户模型中没有调用此字段。 如果要使用原始模型存储名称,则必须将其分别存储为名字和姓氏。 编辑: 如果您只希望表单中的一个字段

  • 我正在使用helm charts创建部署微服务,通过执行helm create创建包含部署、服务和入口的基本图表,但我没有其他配置,如水平pod自动缩放器、pod中断预算。