简体   繁体   English

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

[英]How do I implement intelligent drag and drop from one JTree to another?

I have two JTrees with some mock data in there, what I'm looking to do is to be able to take each 'Job' (15663-1, 15663-2, etc) and make a node for each, with a node for each part underneath it and the components attached to each part underneath that. 我有两个带有一些模拟数据的JTree,我想要做的是能够容纳每个“作业”(15663-1、15663-2等),并为每个节点创建一个节点,并为它下面的每个零件以及该零件下面连接到每个零件的组件。 In two trees, like this: 在两棵树中,如下所示:

+------------------------------+------------------------------+
| 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]    |
+------------------------------+------------------------------+

So that supposing I have two screws in the cover in job A in the 'parts to be shipped' jtree and I don't have anything in jobA in the shipping box, when I drag the screws over to the shipping box it should make an entry for jobA, make an entry for part A and make an entry for the component, then I want it to prompt for the quantity for that component and subtract that quantity from the parts to be shipped jtree. 因此,假设我在“待运输零件” jtree的作业A的盖子上有两个螺丝,而在装运箱中的作业A中没有任何东西,当我将螺丝拖到装运箱上时,它应该jobA的条目,A部分的条目,组件的条目,然后,我希望它提示该组件的数量,并从要运输的零件jtree中减去该数量。

So if I have a job called 1553-4 and it has a cover with four screws and I drag the screws into the shipping box then it should make an entry in the shipping box that says, "x screws" then prompt for the user to enter the number of screws they just packaged, if they package two screws then the jtree should change to reflect the 2 screws remaining for that job. 因此,如果我有一个名为1553-4的工作,并且它的盖子上有四个螺钉,并且将螺钉拖到装运箱中,那么它应该在装运箱中输入“ x螺钉”,然后提示用户输入刚包装的螺钉数量,如果包装了两个螺钉,则jtree应该更改以反映该工作剩余的2个螺钉。

I have read a bunch of different drag and drop tutorials and I have some examples but I just cannot seem to get it. 我已经阅读了一堆不同的拖放教程,并且有一些示例,但似乎无法理解。 Any advice or help would be appreciated. 任何建议或帮助,将不胜感激。

I know that I need to implement a TranferHandler but I'm not sure exactly how, there seems to be too much interface 'magic' going on and I'm really not understanding it. 我知道我需要实现一个TranferHandler,但是我不确定到底是怎么实现的,似乎接口“魔术”正在发生,我真的不理解。

This is what I have, I understand making nodes and such, here is what I have: 这就是我所拥有的,我了解制作节点之类的东西,这就是我所拥有的:

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;
    }
}

I'm just looking for a concise drag and drop example of dragging into a JTree and dragging from a JTree. 我只是在寻找一个简单的拖放示例,将其拖放到JTree中并从JTree中拖动。

A very short, simple introduction to Drag and Drop in Swing in my own words and based on my own experience (which was mainly with drag-and-drop in JDK1.5, so new functionality might already be present). 用我自己的话,根据我自己的经验,对Swing中的拖放进行了非常简短的简单介绍(主要是JDK1.5中的拖放操作,因此可能已经存在新功能)。

There are two parts in a drag-and-drop operation. 拖放操作分为两部分。 First there is the drag from the source component. 首先是来自源组件的拖动。 The TransferHandler of the source component creates a Transferable , which is a container for the data which will be exchanged in the drag-and-drop operation. 所述TransferHandler源组件的创建一个Transferable ,其是用于将在拖和放操作要交换的数据的容器。 Depending on the data, there might be different representations of the data (which are called DataFlavor s). 根据数据,可能会有不同的数据表示形式(称为DataFlavor )。 For example if you drag-and-drop an URL to a text editor, it will most likely add the URL to the current document. 例如,如果将URL拖放到文本编辑器中,则很有可能会将URL添加到当前文档中。 But if you drop it onto a web-browser, you hope it opens that URL. 但是,如果将其放到网络浏览器中,则希望它打开该URL。 So where the first is just interested in plain text, the second might be interested in a more complex object. 因此,在第一个只对纯文本感兴趣的地方,第二个可能对更复杂的对象感兴趣。

The second part is the drop. 第二部分是下降。 First it is decided whether the current location is a good drop target. 首先,确定当前位置是否是良好的放置目标。 It is up to the target component's transfer handler to decide whether it accepts the drop. 由目标组件的传输处理程序决定是否接受删除。 Typically this is achieved by checking whether it can handle the data contained in the Transferable by asking the Transferable for the data for a specific DataFlavor (note: the Flavor must be known be both the source and target component). 通常,这是通过检查是否可以处理Transferable包含的数据来实现的,方法是向Transferable DataFlavor特定DataFlavor的数据(请注意:必须同时知道Flavor是源组件还是目标组件)。 When it accepts the drop and the user releases the mouse, it can proceed with handling the data in the Transferable , and hopefully do something useful with it. 当它接受放置并且用户释放鼠标时,它可以继续处理Transferable的数据,并希望对此做一些有用的事情。

But as always, the Swing tutorials are a very good starting point. 但是与往常一样, Swing教程是一个很好的起点。 After you went through them, you can probably come up with a more detailed question (if you still have any, as your requirement is rather trivial) 在研究了它们之后,您可能会提出一个更详细的问题(如果仍然有任何问题,因为您的要求相当微不足道)

On theory, I think Robin has answered your question good. 从理论上讲,我认为罗宾回答了您的问题很好。 So below is the implementation that I've done. 因此,下面是我完成的实现。 To summarize, the implementation include the top two label and bottom two scrollpanes, drag from left to the right. 总而言之,该实现包括从左向右拖动的顶部两个标签和底部两个滚动窗格。 There are still minor thing like before import happen, a dialog should appear and asking user how much quantity to drop (and then do the arithmetic operation) but I think that can be your homework? 还有一些小问题,例如在导入之前,应该出现一个对话框,询问用户要减少多少数量(然后进行算术运算),但是我认为这可以作为您的功课? ;-) Let me know if you need further help. ;-)让我知道您是否需要进一步的帮助。

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;
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM