2012-03-29 61 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