JavaFx,如何正确覆盖两者:树 Table 视图中的 getChildren() 和 getChildren().add(newNode)

JavaFx, how to correctly override both : getChildren() and getChildren().add(newNode) in a Tree Table View

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

我的第一个方法是 'encapsulate' 方法中的 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. child人的并行列表。
  2. 覆盖“add(...)”
  3. TreeItem
  4. getChildren() 返回的列表

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

在 class 中创建并行 LIST(准确地说是 FXCollections.observableArrayList())的部分是在 PDF 中提出的,我不能回头找一位印度老师练习他的 JavaFx class.

下面的代码只需要复制,所以我已经包含在 'main' class 中,所有底层 classes...我知道这不是最佳实践,但是为了让您尽可能轻松地制作代码 运行,我决定这样做......如果您认为照常制作更好,请告诉我:1 Class = 1 个文件。

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

表现形式 1:树

所以在这两种情况下,我的第一个 children 列表都存在...它们在 ROOT 树项目下,但是:当通过自定义 ObservableList 时,它们没有显示在树中,在 ROOT 下.

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

是不是因为:

表现形式 2:Children / Parent

我已经破解了children/parent!这也与我尝试构建并行LIST并进行自定义getChildren()有关。我想应该是 super.getChildren() 的自定义,但话又说回来,尝试了很多方法都没有成功。如果是这样,我得到的不是我定制的 LIST,而是原始(正确)的。

将不胜感激。

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

}

有时,最好只是仔细观察并从头开始重新启动整个体系结构。这导致了我的情况下可能的最佳答案之一......我希望......它运作良好!

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

正确的方向是 events,在本例中是 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();
    }
}