Java 如何实现从一个JTree到另一个JTree的智能拖放?

Java 如何实现从一个JTree到另一个JTree的智能拖放?,java,swing,drag-and-drop,jtree,Java,Swing,Drag And Drop,Jtree,我有两个JTree,其中包含一些模拟数据,我希望能够接受每个“作业”(15663-1、15663-2等),并为每个“作业”创建一个节点,每个节点下有一个节点,每个零件下有一个节点,每个零件下都有组件。在两棵树上,像这样: +------------------------------+------------------------------+ | PARTS TO BE SHIPPED | SHIPPING BOX | +----------

我有两个JTree,其中包含一些模拟数据,我希望能够接受每个“作业”(15663-1、15663-2等),并为每个“作业”创建一个节点,每个节点下有一个节点,每个零件下有一个节点,每个零件下都有组件。在两棵树上,像这样:

+------------------------------+------------------------------+
| PARTS TO BE SHIPPED          | SHIPPING BOX                 |
+------------------------------+------------------------------+
|[JOB]                         |[JOB]                         |
|+------[part]                 |+------[part]                | 
|        +------[component]    |        +------[component]    |
|        +------[component]    |        +------[component]    |
|+------[part]                 |+------[part]                 |
|        +------[component]    |        +------[component]    |
|[JOB]                         |[JOB]                         |
|+------[part]                 |+------[part]                 |
|        +------[component]    |        +------[component]    |
|        +------[component]    |        +------[component]    |
|+------[part]                 |+------[part]                 |
|        +------[component]    |        +------[component]    |
+------------------------------+------------------------------+
因此,假设我在“待装运零件”jtree中的作业A中有两个螺钉,而装运箱中的作业A中没有任何螺钉,当我将螺钉拖到装运箱时,它应该为作业A创建一个条目,为零件A创建一个条目,为组件创建一个条目,然后我希望它提示该组件的数量,并从要装运的零件中减去该数量

因此,如果我有一个名为1553-4的作业,它有一个带有四个螺钉的盖子,我将螺钉拖到装运箱中,那么它应该在装运箱中输入一个条目,上面写着“x螺钉”,然后提示用户输入他们刚包装的螺钉数量,如果他们包装了两个螺钉,则jtree应更改以反映该作业剩余的两个螺钉

我读过很多不同的拖放教程,我也有一些例子,但我似乎无法理解。如有任何建议或帮助,将不胜感激

我知道我需要实现一个TranferHandler,但我不确定具体如何实现,似乎有太多的接口“魔法”在进行,我真的不理解它

这就是我所拥有的,我理解制作节点,这就是我所拥有的:

package com.protocase.examples;


import java.awt.Dimension;
import java.awt.HeadlessException;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.MutableTreeNode;

/**
 * @author DavidH
 */
public class JTreeExample {
    public static void main(String[] args) {
        addTreesAndDisplay();

    }

    private static void addTreesAndDisplay() throws HeadlessException {
        JFrame frame = new JFrame();
        JPanel panel = new JPanel();


        JTree tree = new JTree(getTreeModel());
        tree.setDragEnabled(true);
        tree.setPreferredSize(new Dimension(200,400));
        JScrollPane scroll = new JScrollPane();
        scroll.setViewportView(tree);
        panel.add(scroll);


        JTree secondTree = new JTree(getTreeModel());
        secondTree.setPreferredSize(new Dimension(200,400));
        secondTree.setDragEnabled(true);
        JScrollPane secondScroll = new JScrollPane();
        secondScroll.setViewportView(secondTree);
        panel.add(secondScroll);


        frame.setContentPane(panel);
        frame.pack();
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    private static DefaultTreeModel getTreeModel() {
        MutableTreeNode root =  new DefaultMutableTreeNode("15663-1");
        DefaultMutableTreeNode cover = new DefaultMutableTreeNode("Cover");
        DefaultMutableTreeNode base = new DefaultMutableTreeNode("Base");
        root.insert(cover, 0);
        root.insert(base, 0);
        cover.insert(new DefaultMutableTreeNode("2x PEMS"), 0);
        cover.insert(new DefaultMutableTreeNode("2x SCREWS"), 0);
        base.insert(new DefaultMutableTreeNode("4x SCREWS"), 0);
        base.insert(new DefaultMutableTreeNode("4x HANDLES"), 0);
        DefaultTreeModel model = new DefaultTreeModel(root);
        return model;
    }
}

我只是想找一个简单的拖放示例,介绍如何在JTree中拖放和从JTree中拖放。

用我自己的话,根据我自己的经验(主要是JDK1.5中的拖放,所以可能已经有了新的功能)

拖放操作分为两部分。首先是源组件的拖动。创建,它是将在拖放操作中交换的数据的容器。根据数据的不同,数据可能有不同的表示形式(称为s)。例如,如果将URL拖放到文本编辑器中,它很可能会将URL添加到当前文档中。但是如果你把它放到网络浏览器上,你希望它能打开这个URL。所以第一个只对纯文本感兴趣,第二个可能对更复杂的对象感兴趣

第二部分是下降。首先,确定当前位置是否是一个良好的投放目标。由目标组件的传输处理程序决定是否接受删除。通常,通过询问
可转移
获取特定
DataFlavor
的数据,检查其是否能够处理
可转移
中包含的数据来实现这一点(注意:
Flavor
必须是源组件和目标组件)。当它接受拖放并且用户释放鼠标时,它可以继续处理
文件中的数据,并希望能用它做一些有用的事情


但和往常一样,这是一个很好的起点。在你仔细阅读之后,你可能会提出一个更详细的问题(如果你还有问题的话,因为你的要求很琐碎)

理论上,我认为罗宾很好地回答了你的问题。下面是我所做的实现。总之,实现包括顶部的两个标签和底部的两个滚动窗格,从左向右拖动。还有一些小事情,比如在导入之前,应该出现一个对话框,询问用户要减少多少数量(然后做算术运算),但我认为这可能是您的家庭作业?;-)如果你需要进一步的帮助,请告诉我

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.TransferHandler;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreePath;

public class JTreeExample extends JPanel
{
    private JTree tree;
    private DefaultTreeModel treeModel;


    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {

            @Override
            public void run()
            {
                createAndShowGUI();             
            }
        });
    }

    private static void createAndShowGUI()
    {
        JFrame frame = new JFrame("My Warehouse");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JTreeExample newContentPane = new JTreeExample();
        newContentPane.setOpaque(true);
        frame.setContentPane(newContentPane);

        frame.pack();
        frame.setVisible(true);
    }

    public JTreeExample()
    {
        setLayout(new GridLayout(1, 3));
        JLabel lbl_parts = new JLabel("PARTS TO BE SHIPPED");       
        tree = new JTree(getTreeModel());
        tree.setDragEnabled(true);        
        tree.setPreferredSize(new Dimension(200,400));
        JScrollPane scroll = new JScrollPane();
        scroll.setViewportView(tree);

        JLabel lbl_ship = new JLabel("SHIPPING BOX");
        treeModel = getTreeModel();
        JTree secondTree = new JTree(treeModel);
        secondTree.setPreferredSize(new Dimension(200,400));        
        secondTree.setTransferHandler(new TransferHandler() {

            @Override
            public boolean importData(TransferSupport support)
            {
                if (!canImport(support))
                {
                    return false;
                }

                JTree.DropLocation dl = (JTree.DropLocation) support.getDropLocation();

                TreePath path = dl.getPath();
                int childIndex = dl.getChildIndex();

                String data;
                try
                {
                    data = (String) support.getTransferable().getTransferData(DataFlavor.stringFlavor);
                }
                catch (UnsupportedFlavorException e)
                {
                    return false;                   
                }
                catch (IOException e)
                {
                    return false;                   
                }

                if (childIndex == -1)
                {
                    childIndex = tree.getModel().getChildCount(path.getLastPathComponent());
                }

                DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(data);
                DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) path.getLastPathComponent();
                treeModel.insertNodeInto(newNode, parentNode, childIndex);

                tree.makeVisible(path.pathByAddingChild(newNode));
                tree.scrollRectToVisible(tree.getPathBounds(path.pathByAddingChild(newNode)));

                return true;
            }

            public boolean canImport(TransferSupport support)
            {
                if (!support.isDrop())
                {
                    return false;                   
                }

                support.setShowDropLocation(true);
                if (!support.isDataFlavorSupported(DataFlavor.stringFlavor))
                {
                    System.err.println("only string is supported");
                    return false;                   
                }

                JTree.DropLocation dl = (JTree.DropLocation) support.getDropLocation();

                TreePath path = dl.getPath();

                if (path == null)
                {
                    return false;                   
                }
                return true;
            }                       
        });
        JScrollPane secondScroll = new JScrollPane();
        secondScroll.setViewportView(secondTree);

        JPanel topPanel = new JPanel(new BorderLayout());
        topPanel.add(lbl_parts, BorderLayout.NORTH);
        topPanel.add(scroll, BorderLayout.CENTER);

        JPanel btmPanel = new JPanel(new BorderLayout());
        btmPanel.add(lbl_ship, BorderLayout.NORTH);
        btmPanel.add(secondScroll, BorderLayout.CENTER);

        add(topPanel);
        add(btmPanel);        

    }

    private static DefaultTreeModel getTreeModel()
    {
        MutableTreeNode root =  new DefaultMutableTreeNode("15663-1");                        

        DefaultMutableTreeNode cover = new DefaultMutableTreeNode("Cover");
        cover.insert(new DefaultMutableTreeNode("2x PEMS"), 0);
        cover.insert(new DefaultMutableTreeNode("2x SCREWS"), 0);
        root.insert(cover, 0);

        DefaultMutableTreeNode base = new DefaultMutableTreeNode("Base");
        base.insert(new DefaultMutableTreeNode("4x SCREWS"), 0);
        base.insert(new DefaultMutableTreeNode("4x HANDLES"), 0);
        root.insert(base, 0);

        DefaultTreeModel model = new DefaultTreeModel(root);
        return model;
    }
}

这是一个很好的解释。我知道我需要扩展TransferHandler,但是我需要扩展DataFlavor吗?因此,我需要扩展源列表上的传输处理程序,让它知道如何将其打包成可转移版本(这是一个拥有对象并扩展可转移的类),然后为目标编写第二个传输处理程序(当然,除非第一个传输处理程序同时知道如何做)?DataFlavor从何而来?DataFlavor只是一种指示
可转移的
中可用的“数据类型”的方法(对于发送方),以及对于接收方请求特定类型的数据的方法。把它想象成图书馆书籍上的标签,你可以说“给我神秘的书”和“给我浪漫的东西”。谢谢,我要看看这些教程。扩展TransferHandler时,有两种canImport实现,一种支持传输,另一种支持传输口味和jcomponent。如果我想将JTree中的一个节点传输到JList,我会覆盖canImport(JComponent comp,[]TransferFlavors)还是canImport(TransferSupport support)?天哪,我不明白人们为什么要传递数据风格。我现在有点明白了,除了TransferHandler部分。这个解决方案对字符串非常有效,并向我展示了importData和canImport的良好实现,但是如果我的节点包含对象(部分),那么我不也需要实现exportData吗?dah,找出并实施所有这些,包括我在回答中提到的一项,都需要付出努力和时间。这就是我们学习和成长的方式。我当然很想完成这个示例代码,但不幸的是,我被繁忙的工作日程所束缚。但是如果我有空的话,我会把上面答案中的代码和其余的要求一起更新。是的,这就足够了,剩下的我自己解决了。我真正不明白的是应该实现/覆盖哪些方法,因为似乎有不同的拖放方式。