The TreeItem API exposes the getChildren() method. The method is not marked final, therefore, I can override it.
The behavior I was expecting is that, if I override this method, I can pass a different observable list then the default one.
The below JFXTreeItem extends TreeItem by overriding getChildren(). However, when used in a treeView, I do not get children displayed on screen.
-----------------------------------------------------------------------------------------------------------------------------------
public class JFXTreeItem<T> extends TreeItem<T> {
private ObservableList<TreeItem<T>> children;
public JFXTreeItem(T value, Node graphic) {
super(value, graphic);
children = FXCollections.observableArrayList();
}
@Override
public ObservableList<TreeItem<T>> getChildren() {
return children;
}
}
-----------------------------------------------------------------------------------------------------------------------------------
A closer look at the TreeItem code reveals two problems:
1- getChildren() in TreeItem is setting a changeListener (which is private and not exposed to subclasses). Therefore any overriding of getChildren() will loose this changeListener. I am referring to the code below from TreeItem :
public ObservableList<TreeItem<T>> getChildren()
{
if (this.children == null) {
this.children = FXCollections.observableArrayList();
this.children.addListener(this.childrenListener);
}
if (this.children.isEmpty()) return this.children;
checkSortState();
return this.children;
}
2- The code of TreeItem does not consistently call getChildren(), and at times will call <this.children>. Since the only exposed API is getChildren(), the value in this.children becomes irrelevant if the developer overrided getChildren(). As an example, the sort method :
void sort()
{
sort(this.children, this.lastComparator, this.lastSortMode);
}
The only way where changing the children list works is by a hack through reflection. Something like the code below:
----------------------------------------------------------------------------------------------------------------------------------------
public JFXTreeItem(T value, Node graphic) {
super(value, graphic);
ObservableList<TreeItem<T>> children = FXCollections.observableArrayList();
try {
Field childrenField = TreeItem.class.getDeclaredField("children");
childrenField.setAccessible(true);
childrenField.set(this, children);
Field declaredField = TreeItem.class.getDeclaredField("childrenListener");
declaredField.setAccessible(true);
children.addListener((ListChangeListener<? super TreeItem<T>>) declaredField.get(this));
} catch (Exception e) {
e.printStackTrace();
}
------------------------------------------------------------------------------------------------------------------------------------------
This code works, and items are displayed on the screen correctly.
However, I don't think as an end developer I should resort to this tweak.
Either the getChildren() remains available for overriding , and the internals of TreeItem get fixed.
Or getChildren() should be declared final, and therefore the developer knows that overriding it won't work.
The behavior I was expecting is that, if I override this method, I can pass a different observable list then the default one.
The below JFXTreeItem extends TreeItem by overriding getChildren(). However, when used in a treeView, I do not get children displayed on screen.
-----------------------------------------------------------------------------------------------------------------------------------
public class JFXTreeItem<T> extends TreeItem<T> {
private ObservableList<TreeItem<T>> children;
public JFXTreeItem(T value, Node graphic) {
super(value, graphic);
children = FXCollections.observableArrayList();
}
@Override
public ObservableList<TreeItem<T>> getChildren() {
return children;
}
}
-----------------------------------------------------------------------------------------------------------------------------------
A closer look at the TreeItem code reveals two problems:
1- getChildren() in TreeItem is setting a changeListener (which is private and not exposed to subclasses). Therefore any overriding of getChildren() will loose this changeListener. I am referring to the code below from TreeItem :
public ObservableList<TreeItem<T>> getChildren()
{
if (this.children == null) {
this.children = FXCollections.observableArrayList();
this.children.addListener(this.childrenListener);
}
if (this.children.isEmpty()) return this.children;
checkSortState();
return this.children;
}
2- The code of TreeItem does not consistently call getChildren(), and at times will call <this.children>. Since the only exposed API is getChildren(), the value in this.children becomes irrelevant if the developer overrided getChildren(). As an example, the sort method :
void sort()
{
sort(this.children, this.lastComparator, this.lastSortMode);
}
The only way where changing the children list works is by a hack through reflection. Something like the code below:
----------------------------------------------------------------------------------------------------------------------------------------
public JFXTreeItem(T value, Node graphic) {
super(value, graphic);
ObservableList<TreeItem<T>> children = FXCollections.observableArrayList();
try {
Field childrenField = TreeItem.class.getDeclaredField("children");
childrenField.setAccessible(true);
childrenField.set(this, children);
Field declaredField = TreeItem.class.getDeclaredField("childrenListener");
declaredField.setAccessible(true);
children.addListener((ListChangeListener<? super TreeItem<T>>) declaredField.get(this));
} catch (Exception e) {
e.printStackTrace();
}
------------------------------------------------------------------------------------------------------------------------------------------
This code works, and items are displayed on the screen correctly.
However, I don't think as an end developer I should resort to this tweak.
Either the getChildren() remains available for overriding , and the internals of TreeItem get fixed.
Or getChildren() should be declared final, and therefore the developer knows that overriding it won't work.