【问题标题】:Drag and Drop in JTree - can importData modify the tree?在 JTree 中拖放 - importData 可以修改树吗?
【发布时间】:2016-01-16 20:28:11
【问题描述】:

我在JTree 组件中拖放时遇到了一些困难。我的拖放是允许用户重新排列树中的节点。我的实现工作正常,但在某些情况下,在其子节点之间移动节点时会出现空指针异常,如下图所示,我将 C 拖到 B 下(在 B 和 A 之间):

该异常的直接原因似乎是在执行 DnD 框架的清理时,被拖动项目的路径因我在树上执行的更改而无效以实现所需的移动。

更具体地说,我得到一个这样的调用堆栈:

javax.swing.plaf.basic.BasicTreeUI#getDropLineRect
java.beans.PropertyChangeSupport#firePropertyChange
...
javax.swing.TransferHandler.DropHandler#cleanup
...
javax.swing.TransferHandler.SwingDropTarget#drop

getDropLineRect 显然是在尝试为树中不再​​存在的路径评估一个矩形,因为在我的 importData 函数中执行移动时该节点已被删除。

我的问题是:这是预期的吗?在 DnD 尚未完成时执行数据更改是否被认为是不安全的,是否应该排队并稍后执行?我在任何文档中都没有看到任何此类要求,并且在任何示例或教程中都没有看到此类异步实现。

【问题讨论】:

    标签: java swing drag-and-drop jtree


    【解决方案1】:

    恕我直言,它不仅可以节省 - 正如您自己所说的那样 - 在下降发生后进行任何数据修改,而且更容易实施。 如果您已经在拖动事件中删除了节点,您需要手头有一个回滚来处理从您的窗口中删除节点或进行非法放置的情况。 另一方面,如果您使用拖动事件只是为了获得成功放置所需的信息并且尚未更改数据中的任何内容,那么前面提到的问题就会消失。在放置时,您将使用从拖动中记住的信息并完成整个操作,或者丢弃放置,这反过来会回滚视觉操作(尽管我在这里不是 100% 确定),因此始终保持一致的状态.

    编辑:“我的问题是:这是预期的吗?”:我认为异常发生在您在拖放之间更改树模型的逻辑中,使得事件调度线程在某个时间不一致从模型中重新绘制它。抱歉无法说明“这就是它的完成方式”,也许其他人从秋千树 dnd doc 中找到了一句话。

    【讨论】:

    • "如果您已经在拖动事件中删除了节点" - importData 不是拖动,而是放置,因此没有取消的风险。例外不在拖放中或在拖放之间,而是在拖放中(但在拖放清理之前)。
    【解决方案2】:

    我仍然不能给出一般性的陈述,但我认为查看标准实现总是一个好主意,在这种情况下,查看 TreeModel。您没有说明您使用哪种 TreeModel。如果你是自定义的,你必须自己触发“tree changed event”,告诉树发生了一些变化。我使用 DefaultTreeModel 从 dnd 的示例中找到了这段摘录。它已准备好运行,我尝试构建您描述的错误,但它似乎工作正常。 “model.insertNodeInto”行会触发 DefaultTreeModel 中的相应事件。 http://www.coderanch.com/t/346509/GUI/java/JTree-drag-drop-tree-Java

    请注意,在 importData 期间和在您的异常发生的清理调用之前触发事件。

    我记得这些事件只是传递给同一个构造函数的参数值不同。我也很难为树触发正确的事件来理解我。在某些时候,我最终从 DefaultTreeModel 源中复制了调用行,并在我的代码中使用了它,因此它可以正常工作。这方面的文档不是很具体。

    public boolean importData(TransferHandler.TransferSupport support) {
        if(!canImport(support)) {
            return false;
        }
        // Extract transfer data.
        DefaultMutableTreeNode[] nodes = null;
        try {
            Transferable t = support.getTransferable();
            nodes = (DefaultMutableTreeNode[])t.getTransferData(nodesFlavor);
        } catch(UnsupportedFlavorException ufe) {
            System.out.println("UnsupportedFlavor: " + ufe.getMessage());
        } catch(java.io.IOException ioe) {
            System.out.println("I/O error: " + ioe.getMessage());
        }
        // Get drop location info.
        JTree.DropLocation dl =
                (JTree.DropLocation)support.getDropLocation();
        int childIndex = dl.getChildIndex();
        TreePath dest = dl.getPath();
        DefaultMutableTreeNode parent =
            (DefaultMutableTreeNode)dest.getLastPathComponent();
        JTree tree = (JTree)support.getComponent();
        DefaultTreeModel model = (DefaultTreeModel)tree.getModel();
        // Configure for drop mode.
        int index = childIndex;    // DropMode.INSERT
        if(childIndex == -1) {     // DropMode.ON
            index = parent.getChildCount();
        }
        // Add data to model.
        for(int i = 0; i < nodes.length; i++) {
            model.insertNodeInto(nodes[i], parent, index++);
        }
        return true;
    }
    

    默认树模型来源:http://developer.classpath.org/doc/javax/swing/tree/DefaultTreeModel-source.html

    编辑:我认为对我也有用的是触发一般的“整个树结构更改”事件,这会导致树从模型状态从头开始构建。我认为它是 Event 构造函数,所有细节都具有空值。当然,这可能需要更多时间来触发实际更改的特定事件。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-06-03
      • 2016-12-24
      • 2011-06-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多