具有拖放功能的JavaFX TreeView会产生异常 [英] JavaFX TreeView with Drag&Drop functionality produces an exception
问题描述
我是JavaFX的新手。我想用Drag& Drop功能实现一个JavaFX TreeView
。
我执行以下操作:我创建一个 TreeView
instance( treeView
)和类型为的根节点ProgramRootTreeItem
(此类扩展 TreeItem
)。
我向根节点添加了一些 ProgramTreeItem
类型的子节点(这也扩展了 TreeItem
)。
之后我将根节点添加到treeView并注册 cellFactory
( DnDTreeCell
)。
DnDTreeCell
实现有关Drag& Drop的所有事件处理。 br>
我可以编译,但是如果我执行我得到一个例外:
I'm new to JavaFX. I would like to implement a JavaFX TreeView
with Drag&Drop functionality.
I do the following: I create a TreeView
instance (treeView
) and a root node of type ProgramRootTreeItem
(this class extends TreeItem
).
To the root node I add some child node of type ProgramTreeItem
(this also extends TreeItem
).
After this I add the root node to treeView and register a cellFactory
(DnDTreeCell
) to it.
DnDTreeCell
implements all the event handling regarding to Drag&Drop.
I can compile, but if I execute I get an exception:
Caused by: java.lang.ClassCastException: java.lang.String cannot be cast to pluginmanagement.ProgramTreeItem
at pluginmanagement.DnDTreeCell.updateItem(DnDTreeCell.java:15)
我完全不知道,为什么我会得到这个例外。
你能帮帮我吗?
提前付款。
I have absolutely no idea, why I get this exception. Can you help me please? Thanx in advance.
DnDTreeApp.java
DnDTreeApp.java
package pluginmanagement;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeItem.TreeModificationEvent;
import javafx.scene.control.TreeView;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.DataFormat;
import javafx.scene.input.DragEvent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;
public class DnDTreeApp extends Application {
public static void main(String[] args) {
Application.launch(DnDTreeApp.class, args);
}
@Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Example Dynamic Tree");
primaryStage.setResizable(true);
final VBox box = new VBox();
box.setFillWidth(false);
Scene scene = new Scene(box);
primaryStage.setScene(scene);
box.getChildren().add(this.getExampleTree());
primaryStage.show();
}
private TreeView<ProgramTreeItem> getExampleTree() {
TreeView<ProgramTreeItem> treeView = new TreeView<ProgramTreeItem>();
ProgramRootTreeItem root = buildTree();
treeView.setCellFactory(new Callback<TreeView<ProgramTreeItem>, TreeCell<ProgramTreeItem>>() {
@Override
public TreeCell call(TreeView<ProgramTreeItem> param) {
System.out.format("\ncall() - param: %s\n", param);
TreeCell cell = new DnDTreeCell(param);
System.out.format("\ncall() - return: %s\n", cell);
return cell;
}
});
treeView.setPrefSize(1000, 750);
treeView.setShowRoot(true);
treeView.setRoot(root);
root.setExpanded(true);
return treeView;
}
private ProgramRootTreeItem buildTree() {
ProgramRootTreeItem root = new ProgramRootTreeItem();
for(int a=1; a<4; a++) {
for(int b=1; b<4; b++) {
ProgramTreeItem child = new ProgramTreeItem("child_" + a + "_" + b);
root.getChildren().add(child);
}
}
return root;
}
}
DnDTreeCell.java
DnDTreeCell.java
package pluginmanagement;
import javafx.event.EventHandler;
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.DragEvent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.TransferMode;
public class DnDTreeCell extends TreeCell<ProgramTreeItem> {
private TreeView<ProgramTreeItem> parentTree;
private ProgramTreeItem item;
public DnDTreeCell(final TreeView<ProgramTreeItem> parentTree) {
System.out.format("\nCTR - DnDTreeCell: [%s]\n", parentTree);
this.parentTree = parentTree;
// ON SOURCE NODE.
setOnDragDetected(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
System.out.println("\nDrag detected on " + item);
if (item == null) {
return;
}
Dragboard dragBoard = startDragAndDrop(TransferMode.MOVE);
ClipboardContent content = new ClipboardContent();
content.put(DataFormat.PLAIN_TEXT, item.toString());
dragBoard.setContent(content);
event.consume();
}
});
setOnDragDone(new EventHandler<DragEvent>() {
@Override
public void handle(DragEvent dragEvent) {
System.out.println("\nDrag done on " + item);
dragEvent.consume();
}
});
// ON TARGET NODE.
// setOnDragEntered(new EventHandler<DragEvent>() {
// @Override
// public void handle(DragEvent dragEvent) {
// System.out.println("Drag entered on " + item);
// dragEvent.consume();
// }
// });
setOnDragOver(new EventHandler<DragEvent>() {
@Override
public void handle(DragEvent dragEvent) {
System.out.println("\nDrag over on " + item);
if (dragEvent.getDragboard().hasString()) {
String valueToMove = dragEvent.getDragboard().getString();
if (!valueToMove.matches(item.toString())) {
// We accept the transfer!!!!!
dragEvent.acceptTransferModes(TransferMode.MOVE);
}
}
dragEvent.consume();
}
});
// setOnDragExited(new EventHandler<DragEvent>() {
// @Override
// public void handle(DragEvent dragEvent) {
// System.out.println("Drag exited on " + item);
// dragEvent.consume();
// }
// });
setOnDragDropped(new EventHandler<DragEvent>() {
@Override
public void handle(DragEvent dragEvent) {
String valueToMove = dragEvent.getDragboard().getString();
ProgramTreeItem itemToMove = search((ProgramTreeItem) parentTree.getRoot(), valueToMove);
ProgramTreeItem newParent = search((ProgramTreeItem) parentTree.getRoot(), item.toString());
if (!newParent.getParent().equals(itemToMove)) {
// Remove from former parent.
System.out.format("\nDrag dropped on " + item + "; ItemToMove: %s; NewParent: %s\n", itemToMove, newParent);
itemToMove.getParent().getChildren().remove(itemToMove);
// Add to new parent.
newParent.getChildren().add(itemToMove);
newParent.setExpanded(true);
} else {
System.out.format("\nDrop not Allowed !\n");
}
dragEvent.consume();
}
});
}
private ProgramTreeItem search(final ProgramTreeItem currentNode, final String valueToSearch) {
ProgramTreeItem result = null;
if (currentNode.toString().matches(valueToSearch)) {
result = currentNode;
} else if (!currentNode.isLeaf()) {
for (Object ch : currentNode.getChildren()) {
result = search((ProgramTreeItem)ch, valueToSearch);
if (result != null) {
break;
}
}
}
return result;
}
@Override
protected void updateItem(ProgramTreeItem item, boolean empty) {
System.out.format("\nupdateItem - [%s]\n", item);
super.updateItem(item, empty);
this.item = item;
String text = (item == null) ? null : item.toString();
setText(text);
}
}
ProgramRootTreeItem.java
ProgramRootTreeItem.java
package pluginmanagement;
public class ProgramRootTreeItem extends ProgramTreeItem {
public ProgramRootTreeItem() {
super("ROOT");
}
@Override
public boolean isRoot() {
return true;
}
@Override
public String toString() {
return "ProgramRootTreeItem_"+getUserObject();
}
}
ProgramTreeItem.java
ProgramTreeItem.java
package pluginmanagement;
import javafx.scene.control.TreeItem;
public class ProgramTreeItem extends TreeItem {
String userObj;
public ProgramTreeItem(String data) {
super(data);
userObj = data;
}
public String getUserObject() {
return userObj;
}
public boolean isRoot() {
return false;
}
@Override
public String toString() {
return "ProgramTreeItem_" + getUserObject();
}
}
推荐答案
你'错误地使用 TreeView
。
您被允许致电的唯一原因
The only reason you're allowed to call
treeView.setRoot(root);
是你的自定义 TreeItem
实现扩展了原始类型而不是指定类型参数。否则编译器会抱怨方法调用。
is that your custom TreeItem
implementations extend the raw type instead of specifying a type parameter. Otherwise the compiler would complain about the method call.
TreeItem
s仅定义树的结构。传递给单元格的实际值存储在 TreeItem
的值
属性中。所有自定义 TreeItem
s商店字符串
s。
TreeItem
s only define the structure of the tree. The actual values that are passed to the cells are stored in the value
property of the TreeItem
. All your custom TreeItem
s store String
s.
当 TreeView
稍后尝试使用 TreeItem
中的值填充它的单元格时,它会获得 String
来自 TreeItem
但是无法使用这些值作为第一个参数调用 updateItem
,因为 DnDTreeCell
需要 ProgramTreeItem
。
When TreeView
later attempts to fill it's cells with the values from the TreeItem
s it gets String
s from the TreeItem
s but is unable to call updateItem
with these values as first parameter, since DnDTreeCell
expects ProgramTreeItem
.
其实你的自定义 TreeItem
类不包含传统 TreeItem
中无法轻易获得的信息:
Actually your custom TreeItem
classes don't contain information that you couldn't get easily from a conventional TreeItem
:
public static boolean isRoot(TreeItem<?> item) {
return item.getParent() == null;
}
我建议使用 TreeItem< String>重写代码
s:
private TreeView<String> getExampleTree() {
TreeView<String> treeView = new TreeView<>();
TreeItem<String> root = buildTree();
treeView.setCellFactory(new Callback<TreeView<String>, TreeCell<String>>() {
@Override
public TreeCell call(TreeView<String> param) {
System.out.format("\ncall() - param: %s\n", param);
TreeCell<String> cell = new DnDTreeCell();
System.out.format("\ncall() - return: %s\n", cell);
return cell;
}
});
treeView.setPrefSize(1000, 750);
treeView.setShowRoot(true);
treeView.setRoot(root);
root.setExpanded(true);
return treeView;
}
private TreeItem<String> buildTree() {
TreeItem<String> root = new TreeItem<>();
for(int a=1; a<4; a++) {
for(int b=1; b<4; b++) {
TreeItem<String> child = new TreeItem<>("child_" + a + "_" + b);
root.getChildren().add(child);
}
}
return root;
}
public class DnDTreeCell extends TreeCell<String> {
public DnDTreeCell() {
// ON SOURCE NODE.
setOnDragDetected(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
TreeItem<String> item = getTreeItem();
if (isEmpty() || item == null || item.getParent() == null) {
return; // don't start drag on empty cell or root cell
}
Dragboard dragBoard = startDragAndDrop(TransferMode.MOVE);
ClipboardContent content = new ClipboardContent();
content.put(DataFormat.PLAIN_TEXT, item.getValue());
dragBoard.setContent(content);
event.consume();
}
});
setOnDragDone(new EventHandler<DragEvent>() {
@Override
public void handle(DragEvent dragEvent) {
dragEvent.consume();
}
});
setOnDragOver(new EventHandler<DragEvent>() {
@Override
public void handle(DragEvent dragEvent) {
if (!(dragEvent.getGestureSource() instanceof TreeCell)
|| ((TreeCell) dragEvent.getGestureSource()).getTreeView() != getTreeView()) {
return; // only allow dragging from this TreeView
}
TreeItem<String> sourceItem = ((TreeCell<String>) dragEvent.getGestureSource()).getTreeItem();
// prevent item from being added to own subtree
TreeItem<String> item = getTreeItem();
while (item != null && item != sourceItem) {
item = item.getParent(); // go up through the tree until dragged item or root is found
}
if (item == null) {
// We accept the transfer!!!!!
dragEvent.acceptTransferModes(TransferMode.MOVE);
}
dragEvent.consume();
}
});
setOnDragDropped(new EventHandler<DragEvent>() {
@Override
public void handle(DragEvent dragEvent) {
TreeItem<String> itemToMove = ((TreeCell<String>) dragEvent.getGestureSource()).getTreeItem();
TreeItem<String> newParent = getTreeItem();
// Remove from former parent.
itemToMove.getParent().getChildren().remove(itemToMove);
// Add to new parent.
newParent.getChildren().add(itemToMove);
newParent.setExpanded(true);
dragEvent.setDropCompleted(true);
dragEvent.consume();
}
});
}
@Override
protected void updateItem(String item, boolean empty) {
System.out.format("\nupdateItem - [%s]\n", item);
super.updateItem(item, empty);
setText(item == null ? "" : item);
}
}
这篇关于具有拖放功能的JavaFX TreeView会产生异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!