Home > front end >  JavaFx, how to correctly override both : getChildren() and getChildren().add(newNode) in a Tree Tabl
JavaFx, how to correctly override both : getChildren() and getChildren().add(newNode) in a Tree Tabl

Time:11-03

Inside an application which uses very extensively TreeTableViews, I came across a need to fire a code each time a child is added to this tree.

My first approach was to 'encapsulate' the myTree.getChildren().add(...) inside a method, for instance :

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

This still leaves the getChildren().add(...) accessible, and my personalized code could thus be bypassed.

I'm looking to get something cleaner, and am stuck trying to override the add(newNode) method of getChildren() in that TreeTableView.

I have made quite some research, and ended up trying to make :

  1. A parallel LIST of children. Which then will be accessible to
  2. Have it's 'add(...) overriden
  3. And that list returned by the getChildren() of the TreeItem

The Overriden part for the getChildren() was inspired very strongly from Code COMMENTED

  • Un-commenting that same part. Then Run. Then fire the button --> enter image description here

  • Manifestation 1 : Tree

    So in both cases, my FIRST children list exists... They are under the ROOT tree item, but : They are not shown in the tree, under the ROOT, when going through customized ObservableList.

    Of what I understood, the mechanism of the TreeTableView is relying on specific calls to the TreeItem list : children, and probably I'm missing something there... Since I made a parallel LIST which is therefore not called... sort of... Did some research and experiments, but no luck so far :-(.

    Is it because :

    • There is something missing on the TreeTableView : CellValueFactory / CellFactory / TreeItems / ...
    • There is something wrong about my ObservableList : Another override I should implement / Wrong ascendant used / Should 'implement' and 'extend' rather than 'extend' ?

    Manifestation 2 : Children / Parent

    I have broken the children/parent ! This also has to do with the fact that I tried to build a parallel LIST and made a customized getChildren(). Should be a customization of super.getChildren() I suppose, but then again, tried many ways without success. And if so, I don't get my customnized LIST but the original (correct) one.

    Help would be greatly appreciated.

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

    CodePudding user response:

    Sometimes, it's best to just take an eagle eye and restart the complete architecture from scratch. This lead to one of the best answers possible in my case... I hope... And it works well !

    The approach of overriding the getChildren.add(...) is a wrong way to go in this case.

    The correct direction is about events and in this case the addEventHandler : enter image description here

    Here is the MCE with the solution implemented :

    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();
        }
    }
    
    • Related