我实际上以一种迂回的方式完成了这一点,尽管它非常灵活,甚至允许您拥有与不同类相关的 TreeItems,同时仍然能够将它们嵌套在彼此中。
最初的检查可能会让人望而生畏,但如果你密切关注它,那将是完全合理的。
您从包含 TreeView 的类开始 - 我们将其称为 TreeViewClass。
这个例子来自我正在开发的一个与 GitHub Gists 交互的程序,它解释了我在类中与之交互的各种对象。
public class TreeViewClass {
public void start() {
TextArea itemInfo = new TextArea();
SplitPane splitPane = new SplitPane(getTreeView(), itemInfo);
splitPane.setDividerPosition(0, .1);
Stage stage = new Stage();
stage.setScene(new Scene(splitPane));
stage.show();
}
private Collection<TreeItem<MyTreeItem>> gistObjectTreeItems() {
Collection<TreeItem<MyTreeItem>> gistTreeItemObjects = FXCollections.observableArrayList();
List<GistObject> gistObjects = GitHubApi.getGistObjectList();
for (GistObject gistObject : gistObjects) {
MyTreeItem myGistObjectTreeItem = new MyTreeItem(gistObject);
TreeItem<MyTreeItem> objectTreeItem = new TreeItem<>(myGistObjectTreeItem);
List<GistFile> gistFileList = gistObject.getFileList();
for(GistFile file : gistFileList) {
TreeItem<MyTreeItem> fileTreeItem = new TreeItem<>(new MyTreeItem(file));
objectTreeItem.getChildren().add(fileTreeItem);
}
gistTreeItemObjects.add(gistObjectTreeItem);
}
return gistTreeItemObjects;
}
/**
We are returning a Collection of TreeItems, each one contains an instance
of MyTreeItem using the constructor that defines the instance as a GistObject.
We then add to each of those objects, more TreeItems that contain the same
class MyTreeItem, only we use the constructor that sets the instance as
GistFile, and obviously, each Gist can have many GistFiles.
**/
private TreeView<MyTreeItem> getTreeView() {
TreeView<MyTreeItem> treeView = new TreeObject(this);
TreeItem<MyTreeItem> treeRoot = new TreeItem<>(new MyTreeItem());
treeRoot.getChildren().addAll(gistObjectTreeItems());
treeView.setRoot(treeRoot);
treeView.setShowRoot(false);
return treeView;
}
/**
getTreeView merely builds the TreeView, then adds to it, the Collection
of MyTreeItems that contain the GistObject and each of those objects
GistFiles. We need a root in the tree, so we add one that also contains
the MyTreeItem class only we use the default constructor, since it is
neither a GistObject or a GistFile, and then we hide it because we
don't need to interact with it.
**/
}
这是主要的 TreeView 扩展类:
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.input.MouseEvent;
public class TreeObject extends TreeView<MyTreeItem> {
public TreeObject() {
super();
addEventHandler(MouseEvent.MOUSE_CLICKED, e->{
if(e.getClickCount() == 1) {
selected = getSelectionModel().getSelectedItem();
if (selected != null) {
gistFile = selected.getValue().getFile();
gist = selected.getValue().getGist();
if (gistFile != null) {
GitHubApi.setSelected(gistFile);
}
else if (gist != null) {
GitHubApi.setSelected(gist);
}
}
}
});
}
TreeItem<MyTreeItem> selected;
GistFile gistFile;
GistObject gist;
}
/**
This is the class that gives us the ability to add all manner of events that we might
want to look for. In this case, I am looking for MOUSE_CLICKED events and notice I can
even check to see how many mouse clicks the user enacted on the TreeItems. When the user
clicks on the TreeItem, the selected object returns the MyTreeItem instance that
is assigned to whichever TreeItem they click on, I can then test to see which way
MyTreeItem has been configured (GistObject or GistFile) and act accordingly.
**/
最后,我们有 MyTreeItem 类:
public class MyTreeItem {
private enum TYPE {
GIST, FILE
}
public MyTreeItem(GistObject gist) {
this.gist = gist;
this.type = TYPE.GIST;
}
public MyTreeItem(GistFile file) {
this.file = file;
this.type = TYPE.FILE;
}
public MyTreeItem(){}
private TYPE type;
private GistObject gist;
private GistFile file;
public boolean isGist() { return type == TYPE.GIST; }
public boolean isFile() { return type == TYPE.FILE; }
public GistObject getGist() { return gist; }
public GistFile getFile() { return file; }
@Override
public String toString() {
if(this.type == TYPE.FILE) return StringUtils.truncate(file.toString(),25);
else if(this.type == TYPE.GIST) return StringUtils.truncate(gist.toString(),25);
return "";
}
}
/**
This should be fairly self-explanatory. This is the MyTreeItem class that can be instantiated
as either a GistObject or a GistFile. Note the toString returns different Strings depending
on how the class was instantiated. The toString() will automatically populate the label
on the TreeItem object that the class is assigned to.
**/