【问题标题】:JavaFx, how to correctly override both : getChildren() and getChildren().add(newNode) in a Tree Table ViewJavaFx,如何正确覆盖两者:树表视图中的 getChildren() 和 getChildren().add(newNode)
【发布时间】:2021-10-26 10:34:16
【问题描述】:

在一个非常广泛地使用TreeTableViews 的应用程序中,我发现每次将一个孩子添加到这棵树时都需要触发一个代码。

我的第一种方法是将myTree.getChildren().add(...)“封装”在一个方法中,例如:

    public boolean addToChildren(TreeItemPlaylist tritPlaylist) {
        boolean resAdd = false;
        resAdd = getChildren().add(tritPlaylist);
        [... personalized code which might affect the resAdd ...]
        return resAdd;
    }

这仍然使getChildren().add(...) 可以访问,因此可以绕过我的个性化代码。

我正在寻找更清洁的东西,并且一直试图覆盖 TreeTableView 中 getChildren()add(newNode) 方法。

我做了相当多的研究,最终尝试做:

  1. 子项的并行列表。然后将可以访问
  2. 是否覆盖了'add(...)
  3. 而该列表由getChildren()TreeItem 返回

getChildren() 的 Overriden 部分的灵感来自于 https://docs.oracle.com/javafx/2/api/javafx/scene/control/TreeItem.html

在一个我找不到的印度老师的 PDF 中建议了在一个将扩展 SimpleListProperty<T> 的类中制作并行列表(准确地说是 FXCollections.observableArrayList())的部分,作为练习他的 JavaFx 类。

只需复制以下代码,因此我将所有底层类都包含在“主”类中...我知道这不是最佳实践,但为了使其尽可能简单你让代码运行,我决定这样做......请告诉我你是否认为像往常一样让它更好:1 Class = 1 File。

问题: 通过在 2 次运行中使用 MCE 来说明我遇到的问题(复选框会更好吗?):

  • 注释部分代码。然后运行。然后触发按钮-->

  • 取消注释同一部分。然后运行。然后触发按钮-->

表现形式 1:树

所以在这两种情况下,我的 FIRST 子列表都存在...它们位于 ROOT 树项下,但是:当通过自定义的 ObservableList 时,它们不会显示在树中,在 ROOT 下。

据我所知,TreeTableView 的机制依赖于对 TreeItem 列表的特定调用:孩子,并且可能我在那里遗漏了一些东西......因为我创建了一个并行列表,因此没有被调用......有点...做了一些研究和实验,但到目前为止还没有运气:-(。

是不是因为:

  • TreeTableView 上缺少某些内容:CellValueFactory / CellFactory / TreeItems / ...
  • 我的 ObservableList 有问题:我应该实施另一个覆盖/使用了错误的升序/应该“实施”和“扩展”而不是“扩展”?

表现 2:孩子/父母

我破坏了孩子/父母!这也与我尝试构建并行 LIST 并制作自定义 getChildren() 的事实有关。我想应该是super.getChildren()的自定义,但话又说回来,尝试了很多方法都没有成功。如果是这样,我得到的不是我的自定义列表,而是原始(正确)列表。

我们将不胜感激。

MCE:

package overrides;

import javafx.application.Application;
import javafx.beans.property.SimpleListProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableRow;
import javafx.scene.control.TreeTableView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class CustomedTypedTreeTableSample extends Application {

    public final class ObservableListOfTreeItemPlaylist<T> extends SimpleListProperty<T> {

        ObservableListOfTreeItemPlaylist() {
            super(FXCollections.observableArrayList());
        }

        @Override
        public boolean add(T element) {
            System.out.println("Adding an element");
            return super.add(element);
        }

    }

    public class TreeItemPlaylist extends TreeItem<TreeTableRowPlaylist> {

        private ObservableListOfTreeItemPlaylist<TreeItem<TreeTableRowPlaylist>> playlistItems = new ObservableListOfTreeItemPlaylist<>();

        public TreeItemPlaylist(TreeTableRowPlaylist treeTableRow) {
            super(treeTableRow);
        }

        ////////////////////////
        // COMMENT / UNCOMMENT the next overridden method to see my problem
        //
        // When commenting : - GUI tree is filled with the correct children. All is fine... but, of course, no customized 'add(...'
        // - When printing to console the children of root : All branches and children are there !
        //
        // When NOT commenting : - The overridden 'add(T element)' of class ObservableListOfTreeItemPlaylist is fired...
        // - GUI tree is left with only the ROOT element
        // - When printing to console only the first children of root are there ! But hey... they are there on the console, but not in the GUI !
        @Override
        public ObservableListOfTreeItemPlaylist<TreeItem<TreeTableRowPlaylist>> getChildren() {
            return playlistItems;
        }
        ////////////////////////
    }

    public class TreeTableRowPlaylist extends TreeTableRow<TreeTableRowPlaylist> {

        // Initialized during constructor
        private SimpleStringProperty name;

        public String getName() {
            return name.get();
        }

        public void setName(String name) {
            this.name.set(name);
        }

        public SimpleStringProperty nameProperty() {
            return name;
        }

        public TreeTableRowPlaylist(String name) {
            super();
            this.name = new SimpleStringProperty(name);
        }
    }

    @SuppressWarnings ("unchecked")
    @Override
    public void start(Stage primaryStage) {
        ///// TreeTableView basics
        // Tree table view
        TreeTableView<TreeTableRowPlaylist> tableView = new TreeTableView<>();
        // Column for the name
        TreeTableColumn<TreeTableRowPlaylist, String> nameColumn = new TreeTableColumn<>("Name");
        nameColumn.setCellValueFactory(param -> param.getValue().getValue().nameProperty());
        // Add column
        tableView.getColumns().addAll(nameColumn);

        ///// Dummy tree building
        tableView.setRoot(new TreeItemPlaylist(new TreeTableRowPlaylist("ROOT")));
        tableView.getRoot().getChildren().add(new TreeItemPlaylist(new TreeTableRowPlaylist("Playlist Leaf 1")));
        tableView.getRoot().getChildren().add(new TreeItemPlaylist(new TreeTableRowPlaylist("Playlist Leaf 2")));
        TreeItemPlaylist aTreeBranchA = new TreeItemPlaylist(new TreeTableRowPlaylist("Playlist Branch A"));
        aTreeBranchA.getChildren().add(new TreeItemPlaylist(new TreeTableRowPlaylist("Playlist Leaf A.1")));
        aTreeBranchA.getChildren().add(new TreeItemPlaylist(new TreeTableRowPlaylist("Playlist Leaf A.2")));
        tableView.getRoot().getChildren().add(aTreeBranchA);

        ///// Testing if overriding is working
        // button to get the list of children
        Button display = new Button("Print out children list to console");
        display.setOnAction(event -> tableView.getRoot().getChildren().forEach(playlist -> printChildrenToConsole(playlist)));

        ///// Setting the GUI
        VBox vbox = new VBox(0, tableView, display);

        Scene scene = new Scene(vbox);

        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private void printChildrenToConsole(TreeItem<TreeTableRowPlaylist> playlist) {
        System.out.println(playlist.getValue().getName());
        if (!playlist.isLeaf()) {
            playlist.getChildren().forEach(childPlaylist -> printChildrenToConsole(childPlaylist));
        }
    }

    public static void main(String[] args) {
        launch(args);
    }

}

【问题讨论】:

  • 你为什么要这样做?您可以使用您的解释编辑问题。此外,通常最好提供minimal reproducible example,但在这种情况下,即使提供了完整的可编译示例,我也不希望您在没有真正令人信服的理由的情况下获得大量帮助。
  • mipa 已经在他的prior answer 中提供了一个合理的策略,为什么不遵循这个策略呢?即“重写 JavaFx TreeItem getChildren 是否添加了一个好主意?” -> 可能不会。
  • 你好@jewelsea,谢谢你的提问。我的另一个问题没有很好地表述。试图删除它但不可能:建议的包装(我从一开始就使用,正如您在我的问题的 cmets 中看到的那样)是我调查的解决方法,而不是正确的解决方案。我想使用继承做得更好并改进我的代码。我认为戴帽子会使问题变得更重,所以我直奔主题。我将对其进行编辑和上下文化。
  • 很公平,我没有对它投反对票,而是对你的努力投了赞成票。我确实认为我以前的 cmets 就如何改进这个问题提供了一些建议。我想说如果推理很明显,只是在没有上下文的情况下提出问题是可以的,但在这种情况下需要上下文,它确实有点像xy problem,如果是这种情况,上下文可以帮助澄清.投票通常是关于问题是否对其他人有用,而不仅仅是提问者,人们经常投票赞成,因为它对他们有用。
  • “我想做得更好并改进我的代码,使用继承。” -> 有时(通常?)继承不会改进代码。这不是通常详细讨论此类事情的地方,但请参阅:Inheritance vs. Aggregation 以供参考。经验丰富的开发人员 (mipa) 查看了您的示例并建议聚合而不是继承,这意味着聚合可能是首选方法。但是,由于您最了解自己的问题领域,因此您的方法可能也有优点。

标签: inheritance javafx treeview overriding treetable


【解决方案1】:

有时,最好只是保持警惕,从头开始重新启动整个架构。在我的情况下,这可能是最好的答案之一......我希望......而且效果很好!

在这种情况下,覆盖 getChildren.add(...) 的方法是错误的。

正确的方向是关于事件,在这种情况下是addEventHandlerhttps://docs.oracle.com/javafx/2/api/javafx/scene/control/TreeItem.html#addEventHandler(javafx.event.EventType,%20javafx.event.EventHandler)

通过使用TreeItem.childrenModificationEvent(),添加到树的每个元素,最终得到想要的结果非常简单:对我的 TreeTableView 的任何添加都将通过我的个性化代码。

这是实施了解决方案的 MCE:

import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableRow;
import javafx.scene.control.TreeTableView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class CustomedTypedTreeTableSample2 extends Application {

    public class TreeTableRowPlaylist extends TreeTableRow<TreeTableRowPlaylist> {

        // Initialized during constructor
        private SimpleStringProperty name;

        public String getName() {
            return name.get();
        }

        public void setName(String name) {
            this.name.set(name);
        }

        public SimpleStringProperty nameProperty() {
            return name;
        }

        public TreeTableRowPlaylist(String name) {
            super();
            this.name = new SimpleStringProperty(name);
        }
    }

    private final class TreeItemPlaylist extends TreeItem<TreeTableRowPlaylist> {
        // private boolean isFirstTimeChildren = true;

        private TreeItemPlaylist(TreeTableRowPlaylist treeTableRowPlaylist) {
            super(treeTableRowPlaylist);

            addEventHandler(TreeItem.childrenModificationEvent(), this::childrenModification);

            // Same code in a 'anonymous' implementation :
//          addEventHandler(TreeItem.childrenModificationEvent(), new EventHandler<TreeModificationEvent<TreeTableRowPlaylist>>() {
//              @Override
//              public void handle(TreeModificationEvent<TreeTableRowPlaylist> event) {
//                  childrenModification(event);
//              }
//          });
        }

        private void childrenModification(TreeModificationEvent<TreeTableRowPlaylist> event) {
            if (event.wasAdded()) {
                for (TreeItem<TreeTableRowPlaylist> item : event.getAddedChildren()) {
                    System.out.println("Node " + item.getValue().getName() + " has been added.");
                }
            }
        }
    }

    private void printChildrenToConsole(TreeItem<TreeTableRowPlaylist> playlist) {
        System.out.println(playlist.getValue().getName());
        if (!playlist.isLeaf()) {
            playlist.getChildren().forEach(childPlaylist -> printChildrenToConsole(childPlaylist));
        }
    }


    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {
        ///// TreeTableView basics
        // Tree table view
        TreeTableView<TreeTableRowPlaylist> tableView = new TreeTableView<>();
        // Column for the name
        TreeTableColumn<TreeTableRowPlaylist, String> nameColumn = new TreeTableColumn<>("Name");
        nameColumn.setCellValueFactory(param -> param.getValue().getValue().nameProperty());
        // Add column
        tableView.getColumns().addAll(nameColumn);

        ///// Dummy tree building
        tableView.setRoot(new TreeItemPlaylist(new TreeTableRowPlaylist("ROOT")));
        tableView.getRoot().getChildren().add(new TreeItemPlaylist(new TreeTableRowPlaylist("Playlist Leaf 1")));
        tableView.getRoot().getChildren().add(new TreeItemPlaylist(new TreeTableRowPlaylist("Playlist Leaf 2")));
        TreeItemPlaylist aTreeBranchA = new TreeItemPlaylist(new TreeTableRowPlaylist("Playlist Branch A"));
        aTreeBranchA.getChildren().add(new TreeItemPlaylist(new TreeTableRowPlaylist("Playlist Leaf A.1")));
        aTreeBranchA.getChildren().add(new TreeItemPlaylist(new TreeTableRowPlaylist("Playlist Leaf A.2")));
        tableView.getRoot().getChildren().add(aTreeBranchA);

        ///// Testing if overriding is working
        // button to get the list of children
        Button display = new Button("Print out children tree to console");
        display.setOnAction(event -> tableView.getRoot().getChildren().forEach(playlist -> printChildrenToConsole(playlist)));

        ///// Setting the GUI
        VBox vbox = new VBox(0, tableView, display);

        Scene scene = new Scene(vbox);

        primaryStage.setScene(scene);
        primaryStage.show();
    }
}

【讨论】:

    猜你喜欢
    • 2012-07-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多