关键是在 TreeView 上定义一个单元工厂,并在单元工厂中将鼠标单击事件处理程序添加到树单元。如此定义的鼠标单击事件处理程序将具有对支持树单元的模型对象的完全访问权限,并且可以对其进行任何操作。
在下面的示例中,当用户单击树中的单元格时,单击处理程序从与单击的单元格相关的底层模型项中提取信息,并将该信息呈现到树下方的标签中。

树应用程序.java
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.*;
import javafx.stage.*;
public class TreeApplication extends Application {
@Override public void start(final Stage stage) throws Exception {
final FXMLLoader loader = new FXMLLoader(
getClass().getResource("treeInterface.fxml")
);
final Parent root = (Parent) loader.load();
stage.setScene(new Scene(root));
stage.show();
}
public static void main(String[] args) { launch(args); }
}
树控制器.java
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.util.Callback;
import javafx.util.Pair;
import java.net.URL;
import java.util.ResourceBundle;
public class TreeController implements Initializable {
@FXML private TreeView<Pair<URIImpl,String>> treeView;
@FXML private Label clickedPair;
@Override public void initialize(URL location, ResourceBundle resources) {
treeView.setCellFactory(new Callback<TreeView<Pair<URIImpl, String>>, TreeCell<Pair<URIImpl, String>>>() {
@Override public TreeCell<Pair<URIImpl, String>> call(TreeView<Pair<URIImpl, String>> treeView) {
return new TreeCell<Pair<URIImpl, String>>() {
final ImageView iconView = new ImageView();
@Override protected void updateItem(final Pair<URIImpl, String> pair, boolean empty) {
super.updateItem(pair, empty);
if (!empty && pair != null) {
setText(
pair.getValue()
);
setGraphic(
iconView
);
iconView.setImage(pair.getKey().getImage());
} else {
setText(null);
setGraphic(null);
}
setOnMouseClicked(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
clickedPair.setText(
"Key: " + pair.getKey() + " Value: " + pair.getValue()
);
}
});
}
};
}
});
loadTreeItems(
createPair("http://www.google.com", "google.com", "Google"),
createPair("http://www.microsoft.com", "microsoft.com", "Microsoft"),
createPair("http://www.yahoo.com", "yahoo.com", "Yahoo")
);
}
private Pair<URIImpl, String> createPair(String uri, String domain, String name) {
return new Pair<>(new URIImpl(uri, domain), name);
}
private void loadTreeItems(Pair<URIImpl,String>... rootItems) {
TreeItem<Pair<URIImpl,String>> root = new TreeItem(createPair("N/A", "", "Root Node"));
root.setExpanded(true);
for (Pair<URIImpl, String> pair: rootItems) {
root.getChildren().add(
new TreeItem<>(
pair
)
);
}
treeView.setRoot(root);
}
}
URIImpl.java
import javafx.scene.image.Image;
class URIImpl {
private final String uri;
private final String domain;
private Image image;
public URIImpl(String uri, String domain) {
this.uri = uri;
this.domain = domain;
}
public String getUri() {
return uri;
}
public String getDomain() {
return domain;
}
public Image getImage() {
if (image == null) {
image = new Image("https://plus.google.com/_/favicon?domain=" + getDomain());
}
return image;
}
@Override
public String toString() {
return "URIImpl{" + "uri->" + uri + '}';
}
}
树接口.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane id="AnchorPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" xmlns:fx="http://javafx.com/fxml" fx:controller="tests.treesample.TreeController">
<TreeView fx:id="treeView" layoutX="0" layoutY="0" prefHeight="193.0" prefWidth="471.0" />
<Label fx:id="clickedPair" layoutX="0" layoutY="200"/>
</AnchorPane>
更新
这工作得很好。但是,一旦我们设置了 CallBack 事件处理程序,图标图像似乎就丢失了。
是的,看起来好像您创建了自己的单元工厂,您还需要在单元工厂中手动设置图形。这是因为如果在树项上设置了图形,默认的树项单元格工厂将从树项中获取图形,而此逻辑不会出现在您的自定义单元格工厂中,除非您在那里对其进行编码。
我更新了示例解决方案中的代码,以展示如何在自定义单元工厂生成的树单元上设置图形。更新后的代码从支持树单元的模型中获取图形图像,但如果首选的话,它同样可以从树项的图形属性中检索它。要从 TreeItem 中获取图形,请按照 blacks0ul 的建议使用以下代码:
TreeItem<Pair<URIImpl, String>> item = getTreeItem();
if (item != null && item.getGraphic() != null) {
setGraphic(item.getGraphic());
}