The model for a single node supplying a hierarchy of values to a control such
as TreeView
. The model may be implemented such that values may be loaded in
memory as they are needed.
The model allows registration of listeners which will be notified as the
number of items changes, their position or if the values themselves change.
Note however that a TreeItem is not a Node, and therefore no visual
events will be fired on the TreeItem. To get these events, it is necessary to
add relevant observers to the TreeCell instances (via a custom cell factory -
see the Cell
class documentation for more details).
In the simplest case, TreeItem instances may be created in memory as such:
TreeItem<String> root = new TreeItem<String>("Root Node");
root.setExpanded(true);
root.getChildren().addAll(
new TreeItem<String>("Item 1"),
new TreeItem<String>("Item 2"),
new TreeItem<String>("Item 3")
);
TreeView<String> treeView = new TreeView<String>(root);
This approach works well for simple tree structures, or when the data is not
excessive (so that it can easily fit in memory). In situations where the size
of the tree structure is unknown (and therefore potentially huge), there is
the option of creating TreeItem instances on-demand in a memory-efficient way.
To demonstrate this, the code below creates a file system browser:
private TreeView buildFileSystemBrowser() {
TreeItem<File> root = createNode(new File("/"));
return new TreeView<File>(root);
}
// This method creates a TreeItem to represent the given File. It does this
// by overriding the TreeItem.getChildren() and TreeItem.isLeaf() methods
// anonymously, but this could be better abstracted by creating a
// 'FileTreeItem' subclass of TreeItem. However, this is left as an exercise
// for the reader.
private TreeItem<File> createNode(final File f) {
return new TreeItem<File>(f) {
// We cache whether the File is a leaf or not. A File is a leaf if
// it is not a directory and does not have any files contained within
// it. We cache this as isLeaf() is called often, and doing the
// actual check on File is expensive.
private boolean isLeaf;
// We do the children and leaf testing only once, and then set these
// booleans to false so that we do not check again during this
// run. A more complete implementation may need to handle more
// dynamic file system situations (such as where a folder has files
// added after the TreeView is shown). Again, this is left as an
// exercise for the reader.
private boolean isFirstTimeChildren = true;
private boolean isFirstTimeLeaf = true;
@Override public ObservableList<TreeItem<File>> getChildren() {
if (isFirstTimeChildren) {
isFirstTimeChildren = false;
// First getChildren() call, so we actually go off and
// determine the children of the File contained in this TreeItem.
super.getChildren().setAll(buildChildren(this));
}
return super.getChildren();
}
@Override public boolean isLeaf() {
if (isFirstTimeLeaf) {
isFirstTimeLeaf = false;
File f = (File) getValue();
isLeaf = f.isFile();
}
return isLeaf;
}
private ObservableList<TreeItem<File>> buildChildren(TreeItem<File> TreeItem) {
File f = TreeItem.getValue();
if (f != null && f.isDirectory()) {
File[] files = f.listFiles();
if (files != null) {
ObservableList<TreeItem<File>> children = FXCollections.observableArrayList();
for (File childFile : files) {
children.add(createNode(childFile));
}
return children;
}
}
return FXCollections.emptyObservableList();
}
};
}
TreeItem Events
TreeItem supports the same event bubbling concept as elsewhere in the
scenegraph. This means that it is not necessary to listen for events on all
TreeItems (and this is certainly not encouraged!). A better, and far more low
cost solution is to instead attach event listeners to the TreeView
root
item. As long as there is a path between
where the event occurs and the root TreeItem, the event will be bubbled to the
root item.
It is important to note however that a TreeItem is not a
Node, which means that only the event types defined in TreeItem will be
delivered. To listen to general events (for example mouse interactions), it is
necessary to add the necessary listeners to the cells
contained
within the TreeView (by providing a TreeView.cellFactoryProperty()
).
The TreeItem class defines a number of events, with a defined hierarchy. These are shown below (follow the links to learn more about each event type):
The indentation shown above signifies the relationship between event types.
For example, all TreeItem event types have
treeNotificationEvent()
as their
parent event type, and the branch
expand
/
collapse
event types are both
treeNotificationEvent()
. For
performance reasons, it is encouraged to listen
to only the events you need to listen to. This means that it is encouraged
that it is better to listen to, for example,
TreeItem.valueChangedEvent()
,
rather than TreeItem.treeNotificationEvent()
.
implements
<T> | The type of the value property within TreeItem. |
TreeView