JavaFX:如何处理从TreeView拖动项目

对于我正在开发的应用程序,我有一个带有(我自己的类型)TreeItems的TreeView。一切正常,我可以按预期显示项目。

现在,我希望能够处理将一个项目从此TreeView拖到应用程序窗口的另一部分,并使其在此执行某些操作。我现在面临两个(至少……)问题:

  1. 每当您在TreeView中单击时,该项目始终处于选中状态。可以预防吗?
  2. 在TreeView上添加MouseEvent侦听器时,我得到了可以检测到拖动并对此做出响应的事件。但是,我无法为鼠标事件确定相应的TreeItem。当然,我需要知道确切的TreeItem才能进行拖动。这可能吗?

我尝试过的一些方法:

  1. 我添加了自己的单元格工厂,即使在处理和使用单元格上的所有鼠标事件时,树中的项目仍处于选中状态???
  2. 如果我向每个单元格添加一个MouseEvent处理程序,我将能够管理拖放,但是鉴于TreeView中可能有成千上万行(可能>> 100,000,并非全部扩展了难度),不是吗?巨大的开销,为TreeView仅拥有一个事件处理程序会更好吗?(但是,如何确定相应的TreeItem?)

TreeView鼠标事件为我提供以下信息:

没有单元格单击: MouseEvent [source = TreeView[id=templateTreeView, styleClass=tree-

view], target = TreeViewSkin$1@32a37c7a[styleClass=cell indexed-cell tree-

cell]'null', eventType = MOUSE_PRESSED, consumed = false, x = 193.0, y =

289.0, z = 0.0, button = PRIMARY, primaryButtonDown, pickResult = PickResult

[node = TreeViewSkin$1@32a37c7a[styleClass=cell indexed-cell tree-cell]'null',

point = Point3D [x = 192.0, y = 8.0, z = 0.0], distance = 1492.820323027551]

单击带有文本“属性”的单元格: MouseEvent [source = TreeView[id=templateTreeView,

styleClass=tree-view], target = TreeViewSkin$1@16aa9102[styleClass=cell

indexed-cell tree-cell]'Attributes', eventType = MOUSE_PRESSED, consumed =

false, x = 76.0, y = 34.0, z = 0.0, button = PRIMARY, primaryButtonDown,

pickResult = PickResult [node = TreeViewSkin$1@16aa9102[styleClass=cell

indexed-cell tree-cell]'Attributes', point = Point3D [x = 75.0, y = 13.0, z =

0.0], distance = 1492.820323027551]

我猜这个秘密在PickResult的Node中,但是从那里我仍然看不到如何到达TreeItem。

希望对此有一个简单的答案…

回答:

您犯了过早优化的罪过:)。

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();

});

在这一点上,它开始看起来像是骇客。

如果要完全禁用选择,则另一个选择可能是为树创建自定义选择模型,该模型总是返回空选择。

以上是 JavaFX:如何处理从TreeView拖动项目 的全部内容, 来源链接: utcz.com/qa/420627.html

回到顶部