Inside an application which uses very extensively TreeTableView
s, 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 :
getChildren()
of the TreeItem
The Overriden part for the getChildren()
was inspired very strongly from
https://docs.oracle.com/javafx/2/api/javafx/scene/control/TreeItem.html
The part of making a parallel LIST (an FXCollections.observableArrayList()
to be precise), inside a class which would extend a SimpleListProperty<T>
, was suggested in a PDF I can't find back, of an Indian teacher as exercise for his JavaFx class.
The following code just needs to be copied, and so I have included inside a 'main' class, all underlying classes... I know this is NOT best practice, but for the sake of making it as easy as possible for you to make the code run, I decided to do so... please tell me if you think it's better to make it as usual : 1 Class = 1 File.
PROBLEM : The problem I have is illustrated by using the MCE in 2 runs (would a checkbox be better to do that?) :
Commenting a part of the code. Then run. Then fire the button -->
Un-commenting that same part. Then Run. Then fire the button -->
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 :
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);
}
}
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
:
https://docs.oracle.com/javafx/2/api/javafx/scene/control/TreeItem.html#addEventHandler(javafx.event.EventType,%20javafx.event.EventHandler)
And by using the TreeItem.childrenModificationEvent()
, added to each element of the tree, it was quite straightforward to end up with the result wanted : any addition to my TreeTableView is going through my personalized code.
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();
}
}