2012-03-29 39 views
5

Tengo dos JTrees con algunos datos falsos, lo que estoy buscando hacer es poder tomar cada 'Trabajo' (15663-1, 15663-2, etc.) y hacer un nodo para cada uno, con un nodo para cada parte debajo de él y los componentes unidos a cada parte debajo de eso. En dos árboles, como este:¿Cómo implemento la función de arrastrar y soltar inteligente de un JTree a otro?

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

Así que suponiendo que tengo dos tornillos en la cubierta de trabajo A en las 'partes para ser enviado' JTree y no tengo nada en Joba en la caja de envío, cuando arrastro los tornillos a la caja de envío, debo hacer una entrada para el trabajo A, hacer una entrada para la parte A y hacer una entrada para el componente, luego quiero que solicite la cantidad para ese componente y restar esa cantidad del partes que se enviarán jtree.

Así que si tengo un trabajo llamado 1553-4 y tiene una cubierta con cuatro tornillos y los arrastre a la caja de envío, entonces debe hacer una entrada en la caja de envío que diga "x tornillos" y luego preguntar para que el usuario ingrese la cantidad de tornillos que acaba de empaquetar, si empacan dos tornillos, el jtree debe cambiar para reflejar los 2 tornillos restantes para ese trabajo.

He leído un montón de diferentes tutoriales de arrastrar y soltar y tengo algunos ejemplos, pero parece que no puedo obtenerlo. Cualquier consejo o ayuda será apreciada.

Sé que tengo que implementar un TransferHandler pero no estoy seguro de cómo exactamente, parece que hay demasiada 'magia' en la interfaz y realmente no lo estoy entendiendo.

Esto es lo que tengo, entiendo que hacen tales nodos y, esto es lo que tengo:

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

sólo estoy buscando un lastre concisa y el ejemplo de arrastrar a caer en un JTree y arrastrando desde un JTree.

Respuesta

3

Una introducción muy breve y sencilla a Arrastrar y Soltar en Swing en mis propias palabras y en base a mi propia experiencia (que fue principalmente con arrastrar y soltar en JDK1.5, por lo que la nueva funcionalidad podría estar presente).

Hay dos partes en una operación de arrastrar y soltar. Primero está el arrastre del componente fuente. El TransferHandler of the source component crea un Transferable, que es un contenedor para los datos que se intercambiarán en la operación de arrastrar de colocar. Dependiendo de los datos, puede haber diferentes representaciones de los datos (que se llaman DataFlavor s). Por ejemplo, si arrastra y suelta una URL en un editor de texto, lo más probable es que agregue la URL al documento actual. Pero si lo coloca en un navegador web, espera que abra esa URL. Entonces, cuando el primero solo está interesado en texto plano, el segundo podría estar interesado en un objeto más complejo.

La segunda parte es la gota. Primero, se decide si la ubicación actual es un buen objetivo de caída. Depende del controlador de transferencia del componente de destino decidir si acepta la caída. Por lo general, esto se logra al verificar si puede manejar los datos contenidos en el Transferable al solicitar Transferable para los datos de un DataFlavor específico (nota: el Flavor debe conocerse tanto como componente de origen como de destino). Cuando acepta la caída y el usuario suelta el mouse, puede continuar con el manejo de los datos en el Transferable, y con suerte hacer algo útil con él.

Pero como siempre, el Swing tutorials es un muy buen punto de partida.Después de que los haya revisado, probablemente se le ocurra una pregunta más detallada (si todavía la tiene, ya que su requisito es bastante trivial)

+0

Esta es una gran explicación. Sé que necesito extender TransferHandler pero ¿necesito extender DataFlavor? Así que necesito extender Transfer Handler en mi lista fuente y hacer que sepa qué hacer para empaquetarlo en una versión transferible (que es una clase que posee un objeto y se extiende Transferible) y luego escribir un segundo manejador de transferencia para el destino (a menos que, por supuesto, el primer manejador de transferencias sepa cómo hacer ambas cosas). ¿Dónde entran los Dataflavors? – davidahines

+1

El DataFlavor es solo una forma de indicar qué tipo de datos está disponible en el 'Transferible' (para el remitente), y para que el receptor solicite un tipo particular de datos. Piense en ello como una especie de etiqueta como en un libro de la biblioteca, donde puede decir "dame el libro de misterio" frente a "dame las cosas románticas" – Robin

+0

Gracias, voy a ver estos tutoriales. – davidahines

2

En teoría, creo que Robin ha respondido bien a su pregunta. Así que abajo está la implementación que hice. Para resumir, la implementación incluye las dos etiquetas principales y las dos pantallas deslizantes inferiores, arrastre de izquierda a derecha. Todavía hay algo menor como que antes de que suceda la importación, debería aparecer un diálogo y preguntarle al usuario cuánta cantidad debe soltar (y luego hacer la operación aritmética) pero creo que esa puede ser su tarea. ;-) Avíseme si necesita ayuda adicional.

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

Esta solución funciona muy bien para cadenas de caracteres y me muestra lo que hace una buena implementación de importData y canImport, pero si mis nodos tienen objetos (partes), entonces ¿no necesito implementar exportData también? – davidahines

+2

dah, tomará esfuerzo y tiempo encontrar e implementar todos estos e incluir el que mencioné en la respuesta. Así es como aprendemos y crecemos. Definitivamente me encantaría completar este código de muestra. Desafortunadamente, me vinculé con un horario de trabajo agitado. Pero actualizaré el código en la respuesta anterior con el resto de los requisitos si tengo tiempo libre. – Jasonw

+0

Sí, eso es más que suficiente, he trabajado el resto por mi cuenta. Lo que realmente no entendí es qué métodos implementar/anular, ya que parece que hay diferentes maneras de arrastrar y soltar. – davidahines