具有拖放功能的 JavaFX 树视图生成异常



我是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: %sn", param);
                TreeCell cell = new DnDTreeCell(param);
                System.out.format("ncall() - return: %sn", 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: %sn", 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();
    }
}

程序树项.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

允许您致电的唯一原因

treeView.setRoot(root);

是自定义TreeItem实现扩展原始类型而不是指定类型参数。否则,编译器会抱怨方法调用。

TreeItem只定义树的结构。传递给单元格的实际值存储在TreeItemvalue 属性中。您所有的自定义TreeItem存储String

TreeView稍后尝试用 TreeItem s 中的值填充它的单元格时,它会从 TreeItem s 中获取 String s,但无法将这些值作为第一个参数调用updateItem,因为DnDTreeCell期望ProgramTreeItem

实际上,您的自定义TreeItem类不包含您无法从传统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: %sn", param);
            TreeCell<String> cell = new DnDTreeCell();
            System.out.format("ncall() - return: %sn", 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);
    }
}

最新更新