【发布时间】:2018-04-20 21:29:06
【问题描述】:
我是 JavaFX 的新手。我想实现一个具有拖放功能的 JavaFX TreeView。
我执行以下操作:我创建一个 TreeView 实例 (treeView) 和一个 ProgramRootTreeItem 类型的根节点(此类扩展 TreeItem)。
我向根节点添加了一些 ProgramTreeItem 类型的子节点(这也扩展了 TreeItem)。
在此之后,我将根节点添加到 treeView 并向其注册 cellFactory (DnDTreeCell)。DnDTreeCell 实现有关拖放的所有事件处理。
我可以编译,但如果我执行,我会得到一个异常:
Caused by: java.lang.ClassCastException: java.lang.String cannot be cast to pluginmanagement.ProgramTreeItem
at pluginmanagement.DnDTreeCell.updateItem(DnDTreeCell.java:15)
我完全不知道为什么会出现这个异常。 你能帮我吗? 提前谢谢。
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
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
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
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();
}
}
【问题讨论】:
-
我不能完全遵循整个逻辑,但我很确定
TreeCell的类型参数应该是数据的类型,而不是TreeItem。在您的情况下,由于ProgramTreeItem是(隐式)TreeItem<String>,DnDTreeCell可能应该扩展TreeCell<String>而不是TreeCell<ProgramTreeItem>。