2012-03-29 56 views
5

我有两个JTree,里面有一些模拟数据,我想要做的是能够把每个'Job'(15663-1,15663-2等)和每个节点做成一个节点节点下面的每个部分以及附着在其下面的每个部分的组件。在两棵树,就像这样:如何实现从一个JTree到另一个JTree的智能拖放?

+------------------------------+------------------------------+ 
| 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的,我没有在包装箱有什么的职吧,当我将螺丝拖到装运箱时,它应该为jobA输入一个条目,为A部分输入一个条目并为该部件输入一个条目,然后我希望它提示输入该部件的数量,并从该部分减去该数量零件将被运送jtree。

因此,如果我有一个叫做1553-4的工作,它有一个带有四个螺丝的盖子,我将这些螺丝拖入装运箱,然后它应该在装运箱中输入一个字样,提示“x个螺丝钉”,然后提示用户可以输入他们刚包装好的螺丝的数量,如果他们包装两个螺丝,那么jtree应该改变以反映剩余的2个螺丝。

我读了一堆不同的拖放教程,我有一些例子,但我似乎无法得到它。任何意见或帮助,将不胜感激。

我知道,我需要实现一个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。

回答

3

以我自己的语言和基于我自己的经验(主要是在JDK1.5中进行拖放操作,因此可能已经存在新功能)对Swing中的拖放进行简短的介绍。

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

第二部分是下降。首先决定当前位置是否是一个很好的下降目标。由目标组件的传输处理程序决定是否接受丢弃。 (:所述Flavor必须是源和目标组件的形状是已知的注)典型地,这是通过检查它是否可以通过询问Transferable用于数据为特定DataFlavor处理包含在Transferable的数据来实现。当它接受放弃并且用户释放鼠标时,它可以继续处理Transferable中的数据,并希望做一些有用的事情。

但一如既往的Swing tutorials是一个非常好的起点。经过他们,你可能会想出一个更详细的问题(如果你还有任何问题,因为你的要求是微不足道的)

+0

这是一个很好的解释。我知道我需要扩展TransferHandler,但是我需要扩展DataFlavor吗?所以我需要在我的源列表上扩展Transfer Handler,并让它知道如何将它打包到一个可转换的版本中(这是一个拥有对象并扩展Transferable的类),然后为目的地编写第二个传输处理程序除非第一个传输处理程序知道如何做到这一点)? Dataflavors在哪里进来? – davidahines 2012-04-03 20:47:36

+1

DataFlavor只是一种方法,用于指示'Transferable'(用于发送方)中可用的'种类'数据,以及接收方请求特定类型的数据。可以把它想像成一种类似于图书馆书籍的标签,在那里你可以说“给我神秘书”与“给我浪漫的东西” – Robin 2012-04-03 21:29:54

+0

谢谢,我将会看看这些教程。 – davidahines 2012-04-03 21:55:33

2

理论上,我认为罗宾已经回答你的问题很好。下面是我所做的实现。总而言之,实现包括顶部两个标签和底部两个滚动条,从左向右拖动。在导入发生之前还有一些小问题,会出现一个对话框,询问用户需要丢弃多少数量(然后进行算术运算),但我认为这可能是您的功课? ;-)让我知道你是否需要进一步的帮助。

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

这个解决方案对于字符串非常适用,并且向我展示了importData和canImport的一个很好的实现,但是如果我的节点持有对象(部分),那么我不需要实现exportData吗? – davidahines 2012-04-04 17:26:41

+2

dah,需要花费精力和时间来找出并实施所有这些,包括我在答案中提到的那个。这就是我们学习和成长的方式。我很乐意完成这个示例代码,不幸的是忙得不可开交的工作时间表。但是如果我有空闲时间的话,我会在上面的答案中更新代码和其余的要求。 – Jasonw 2012-04-05 01:36:18

+0

是的,这已经足够了,我自己就完成了其余的工作。我真的不明白的是,实现/覆盖哪些方法好像有不同的拖放方法。 – davidahines 2012-04-05 15:06:47