您犯了过早优化的罪过:)。
TreeCell基本上只为中的 当前可见
项创建
TreeView。当您展开或折叠树中的节点时,或者在滚动时,这些
TreeCells被重用以显示不同的
TreeItems。这是
updateItem(...)和中类似方法的目的
TreeCell;当该
TreeCell实例显示的项目更改时,将调用它们。
TreeCell我的系统上的A 高约1/4英寸;要显示100,000
TreeCell秒,将需要一台超过2,000英尺/
630米高的显示器。那时,您可能比一些额外的侦听器有更严重的内存分配问题…。但是无论如何,仅当特定单元格上发生事件时,才会调用侦听器,并且与单元本身,因此,除非您有任何直接的证据在单元上注册侦听器(如您所观察到的那样,可大大降低代码复杂性),否则会对性能产生不利影响,因此应使用“每个单元侦听器”方法。
这是一个树的示例,其中包含1,000,000个
Integer值的树项。它跟踪
TreeCell创建的s
的数量(在我的系统上,我设置的窗口大小似乎从未超过20)。它还显示一个标签。您可以将值从树中拖到标签上,标签将显示在那里滴下的所有值的总计。
import java.util.stream.IntStream;import javafx.application.Application;import javafx.beans.property.IntegerProperty;import javafx.beans.property.SimpleIntegerProperty;import javafx.concurrent.Task;import javafx.geometry.Insets;import javafx.geometry.Pos;import javafx.scene.Scene;import javafx.scene.control.Label;import javafx.scene.control.TreeCell;import javafx.scene.control.TreeItem;import javafx.scene.control.TreeView;import javafx.scene.input.ClipboardContent;import javafx.scene.input.DataFormat;import javafx.scene.input.Dragboard;import javafx.scene.input.TransferMode;import javafx.scene.layout.BorderPane;import javafx.stage.Stage;public class TreeViewNoSelection extends Application { private static int cellCount = 0 ; private final DataFormat objectDataFormat = new DataFormat("application/x-java-serialized-object"); @Override public void start(Stage primaryStage) { TreeView<Integer> tree = new TreeView<>(); tree.setShowRoot(false); Task<TreeItem<Integer>> buildTreeTask = new Task<TreeItem<Integer>>() { @Override protected TreeItem<Integer> call() throws Exception { TreeItem<Integer> treeRoot = new TreeItem<>(0); IntStream.range(1, 10).mapToObj(this::createItem) .forEach(treeRoot.getChildren()::add); return treeRoot ; } private TreeItem<Integer> createItem(int value) { TreeItem<Integer> item = new TreeItem<>(value); if (value < 100_000) { for (int i = 0; i < 10; i++) { item.getChildren().add(createItem(value * 10 + i)); } } return item ; } }; tree.setCellFactory(tv -> new TreeCell<Integer>() { { System.out.println("Cells created: "+(++cellCount)); setonDragDetected(e -> { if (! isEmpty()) { Dragboard db = startDragAndDrop(TransferMode.COPY); ClipboardContent cc = new ClipboardContent(); cc.put(objectDataFormat, getItem()); db.setContent(cc); Label label = new Label(String.format("Add %,d", getItem())); new Scene(label); db.setDragView(label.snapshot(null, null)); } }); } @Override public void updateItem(Integer value, boolean empty) { super.updateItem(value, empty); if (empty) { setText(null); } else { setText(String.format("%,d", value)); } } }); IntegerProperty total = new SimpleIntegerProperty(); Label label = new Label(); label.textProperty().bind(total.asString("Total: %,d")); label.setonDragOver(e -> e.acceptTransferModes(TransferMode.COPY)); // in real life use a CSS pseudoclass and external CSS file for the background: label.setonDragEntered(e -> label.setStyle("-fx-background-color: yellow;")); label.setonDragExited(e -> label.setStyle("")); label.setonDragDropped(e -> { Dragboard db = e.getDragboard(); if (db.hasContent(objectDataFormat)) { Integer value = (Integer) db.getContent(objectDataFormat); total.set(total.get() + value); e.setDropCompleted(true); } }); BorderPane.setMargin(label, new Insets(10)); label.setMaxWidth(Double.MAX_VALUE); label.setAlignment(Pos.CENTER); BorderPane root = new BorderPane(new Label("Loading...")); buildTreeTask.setonSucceeded(e -> { tree.setRoot(buildTreeTask.getValue()); root.setCenter(tree); root.setBottom(label); }); primaryStage.setScene(new Scene(root, 250, 400)); primaryStage.show(); Thread t = new Thread(buildTreeTask); t.setDaemon(true); t.start(); } public static void main(String[] args) { launch(args); }}对于选择问题:我会问你为什么要这样做;这会带来不寻常的用户体验。问题可能在于,管理选择的“内置”事件处理程序在定义的处理程序之前被调用,因此在您使用该事件时,选择已被更改。您可以尝试添加事件过滤器:
cell.addEventFilter(MouseEvent.MOUSE_PRESSED, Event::consume);
但这也会禁用扩展/折叠树中的节点。
因此,您可以尝试以下操作:
cell.addEventFilter(MouseEvent.MOUSE_PRESSED, e -> { if (getTreeItem() != null) { Object target = e.getTarget(); if (target instanceof Node && ((Node)target).getStyleClass().contains("arrow")) { getTreeItem().setExpanded(! getTreeItem().isExpanded()); } } e.consume();});在这一点上,它开始看起来像是骇客。
如果要完全禁用选择,则另一个选择可能是为树创建自定义选择模型,该模型总是返回空选择。



