具有拖放功能的JavaFX TreeView会产生异常 [英] JavaFX TreeView with Drag&Drop functionality produces an exception

查看:198
本文介绍了具有拖放功能的JavaFX TreeView会产生异常的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是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。

TreeItems 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 TreeItems store Strings.

TreeView 稍后尝试使用 TreeItem 中的值填充它的单元格时,它会获得 String 来自 TreeItem 但是无法使用这些值作为第一个参数调用 updateItem ,因为 DnDTreeCell 需要 ProgramTreeItem

When TreeView later attempts to fill it's cells with the values from the TreeItems it gets Strings from the TreeItems 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆