使用JTree和模态对话框进行Java拖放

时间:2011-01-19 22:20:56

标签: java swing drag-and-drop jtree

我想在文件系统支持的JTree上实现拖放,以允许用户轻松移动和复制文件。当用户尝试将文件移动或复制到已包含同名文件的目录时,我想显示一个确认对话框。

java在这方面实施拖拽有一个明显的缺陷(或者也许是我理解它的一个缺陷)。在dnd期间显示模态对话框会导致我调用TransferHandler的exportDone方法,同时我仍在确认importData方法中的内部删除。

我附上了一个说明问题的示例应用程序。它不依赖于文件系统支持的JTree,为了简单起见,它需要确认每个drop。如果拖动n drop 1元素,您会注意到它已复制到其目标但未从其原始位置删除。如果你拖动n drop 2元素,你可以清楚地看到在importData完成之前调用了exportDone。

如何在必要时对每个元素进行用户确认时,如何在Java中实现可靠的拖放?

   import java.awt.*;
   import java.awt.datatransfer.*;
   import java.awt.dnd.*;
   import java.util.*;
   import java.util.List;
   import javax.swing.*;
   import javax.swing.tree.*;

   public class TreeDragAndDrop {
       private JScrollPane getContent() {
           JTree tree = new JTree();
           tree.setDragEnabled(true);
           tree.setDropMode(DropMode.ON_OR_INSERT);
           tree.setTransferHandler(new TreeTransferHandler());
           tree.getSelectionModel().setSelectionMode(TreeSelectionModel.CONTIGUOUS_TREE_SELECTION);
           expandTree(tree);
           return new JScrollPane(tree);
       }

       private void expandTree(JTree tree) {
           DefaultMutableTreeNode root = (DefaultMutableTreeNode) tree.getModel().getRoot();
           Enumeration e = root.breadthFirstEnumeration();
           while (e.hasMoreElements()) {
               DefaultMutableTreeNode node = (DefaultMutableTreeNode) e.nextElement();
               if (node.isLeaf())
                   continue;
               int row = tree.getRowForPath(new TreePath(node.getPath()));
               tree.expandRow(row);
           }
       }

       public static void main(String[] args) {
           JFrame f = new JFrame();
           f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
           f.add(new TreeDragAndDrop().getContent());
           f.setSize(400, 400);
           f.setLocation(200, 200);
           f.setVisible(true);
       }
   }

   class TreeTransferHandler extends TransferHandler {
       DataFlavor nodesFlavor;
       DataFlavor[] flavors = new DataFlavor[1];
       DefaultMutableTreeNode[] nodesToRemove;

       public TreeTransferHandler() {
           try {
               String mimeType = DataFlavor.javaJVMLocalObjectMimeType + ";class=\"" + javax.swing.tree.DefaultMutableTreeNode[].class.getName() + "\"";
               nodesFlavor = new DataFlavor(mimeType);
               flavors[0] = nodesFlavor;
           } catch (ClassNotFoundException e) {
               System.out.println("ClassNotFound: " + e.getMessage());
           }
       }

       public boolean canImport(TransferHandler.TransferSupport support) {
           if (!support.isDrop()) {
               return false;
           }
           support.setShowDropLocation(true);
           if (!support.isDataFlavorSupported(nodesFlavor)) {
               return false;
           }
           // Do not allow a drop on the drag source selections.
           JTree.DropLocation dl = (JTree.DropLocation) support.getDropLocation();
           JTree tree = (JTree) support.getComponent();
           int dropRow = tree.getRowForPath(dl.getPath());
           int[] selRows = tree.getSelectionRows();
           for (int i = 0; i < selRows.length; i++) {
               if (selRows[i] == dropRow) {
                   return false;
               }
           }
           // Do not allow MOVE-action drops if a non-leaf node is
           // selected unless all of its children are also selected.
           int action = support.getDropAction();
           if (action == MOVE) {
               return haveCompleteNode(tree);
           }
           // Do not allow a non-leaf node to be copied to a level
           // which is less than its source level.
           TreePath dest = dl.getPath();
           DefaultMutableTreeNode target = (DefaultMutableTreeNode) dest.getLastPathComponent();
           TreePath path = tree.getPathForRow(selRows[0]);
           DefaultMutableTreeNode firstNode = (DefaultMutableTreeNode) path.getLastPathComponent();
           if (firstNode.getChildCount() > 0 && target.getLevel() < firstNode.getLevel()) {
               return false;
           }
           return true;
       }

       private boolean haveCompleteNode(JTree tree) {
           int[] selRows = tree.getSelectionRows();
           TreePath path = tree.getPathForRow(selRows[0]);
           DefaultMutableTreeNode first = (DefaultMutableTreeNode) path.getLastPathComponent();
           int childCount = first.getChildCount();
           // first has children and no children are selected.
           if (childCount > 0 && selRows.length == 1)
               return false;
           // first may have children.
           for (int i = 1; i < selRows.length; i++) {
               path = tree.getPathForRow(selRows[i]);
               DefaultMutableTreeNode next = (DefaultMutableTreeNode) path.getLastPathComponent();
               if (first.isNodeChild(next)) {
                   // Found a child of first.
                   if (childCount > selRows.length - 1) {
                       // Not all children of first are selected.
                       return false;
                   }
               }
           }
           return true;
       }

       protected Transferable createTransferable(JComponent c) {
           JTree tree = (JTree) c;
           TreePath[] paths = tree.getSelectionPaths();
           if (paths != null) {
               // Make up a node array of copies for transfer and
               // another for/of the nodes that will be removed in
               // exportDone after a successful drop.
               List<DefaultMutableTreeNode> copies = new ArrayList<DefaultMutableTreeNode>();
               List<DefaultMutableTreeNode> toRemove = new ArrayList<DefaultMutableTreeNode>();
               DefaultMutableTreeNode node = (DefaultMutableTreeNode) paths[0].getLastPathComponent();
               DefaultMutableTreeNode copy = copy(node);
               copies.add(copy);
               toRemove.add(node);
               for (int i = 1; i < paths.length; i++) {
                   DefaultMutableTreeNode next = (DefaultMutableTreeNode) paths[i].getLastPathComponent();
                   // Do not allow higher level nodes to be added to list.
                   if (next.getLevel() < node.getLevel()) {
                       break;
                   } else if (next.getLevel() > node.getLevel()) { // child node
                       copy.add(copy(next));
                       // node already contains child
                   } else { // sibling
                       copies.add(copy(next));
                       toRemove.add(next);
                   }
               }
               DefaultMutableTreeNode[] nodes = copies.toArray(new DefaultMutableTreeNode[copies.size()]);
               nodesToRemove = toRemove.toArray(new DefaultMutableTreeNode[toRemove.size()]);
               return new NodesTransferable(nodes);
           }
           return null;
       }

       /** Defensive copy used in createTransferable. */
       private DefaultMutableTreeNode copy(TreeNode node) {
           return new DefaultMutableTreeNode(node);
       }

       protected void exportDone(JComponent source, Transferable data, int action) {
           System.out.println("exportDone started");
           if ((action & MOVE) == MOVE) {
               JTree tree = (JTree) source;
               DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
               // Remove nodes saved in nodesToRemove in createTransferable.
               for (int i = 0; i < nodesToRemove.length; i++) {
                   model.removeNodeFromParent(nodesToRemove[i]);
               }
           }
           System.out.println("exportDone finished");
       }

       public int getSourceActions(JComponent c) {
           return COPY_OR_MOVE;
       }

       public boolean importData(TransferHandler.TransferSupport support) {
           System.out.println("importData started");
           if (!canImport(support)) {
               return false;
           }
           // Extract transfer data.
           DefaultMutableTreeNode[] nodes = null;
           try {
               Transferable t = support.getTransferable();
               nodes = (DefaultMutableTreeNode[]) t.getTransferData(nodesFlavor);
           } catch (UnsupportedFlavorException ufe) {
               System.out.println("UnsupportedFlavor: " + ufe.getMessage());
           } catch (java.io.IOException ioe) {
               System.out.println("I/O error: " + ioe.getMessage());
           }
           // Get drop location info.
           JTree.DropLocation dl = (JTree.DropLocation) support.getDropLocation();
           int childIndex = dl.getChildIndex();
           TreePath dest = dl.getPath();
           DefaultMutableTreeNode parent = (DefaultMutableTreeNode) dest.getLastPathComponent();
           JTree tree = (JTree) support.getComponent();
           DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
           // Configure for drop mode.
           int index = childIndex; // DropMode.INSERT
           if (childIndex == -1) { // DropMode.ON
               index = parent.getChildCount();
           }
           // Add data to model.
           for (int i = 0; i < nodes.length; i++) {
               if (JOptionPane.showConfirmDialog(null, "Confirm drop of " + nodes[i].toString(), "Confirm Drop", JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION) {
                   model.insertNodeInto(nodes[i], parent, index++);
               }
           }
           System.out.println("importData finished");
           return true;
       }

       public String toString() {
           return getClass().getName();
       }

       public class NodesTransferable implements Transferable {
           DefaultMutableTreeNode[] nodes;

           public NodesTransferable(DefaultMutableTreeNode[] nodes) {
               this.nodes = nodes;
           }

           public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException {
               if (!isDataFlavorSupported(flavor))
                   throw new UnsupportedFlavorException(flavor);
               return nodes;
           }

           public DataFlavor[] getTransferDataFlavors() {
               return flavors;
           }

           public boolean isDataFlavorSupported(DataFlavor flavor) {
               return nodesFlavor.equals(flavor);
           }
       }
   }

1 个答案:

答案 0 :(得分:-1)

您只需在导入后进行删除即可。

import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

import javax.swing.DropMode;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.TransferHandler;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;

public class TreeDragAndDrop {
    private JScrollPane getContent() {
        JTree tree = new JTree();
        tree.setDragEnabled(true);
        tree.setDropMode(DropMode.ON_OR_INSERT);
        tree.setTransferHandler(new TreeTransferHandler());
        tree.getSelectionModel().setSelectionMode(TreeSelectionModel.CONTIGUOUS_TREE_SELECTION);
        expandTree(tree);
        return new JScrollPane(tree);
    }

    private void expandTree(JTree tree) {
        DefaultMutableTreeNode root = (DefaultMutableTreeNode) tree.getModel().getRoot();
        Enumeration e = root.breadthFirstEnumeration();
        while (e.hasMoreElements()) {
            DefaultMutableTreeNode node = (DefaultMutableTreeNode) e.nextElement();
            if (node.isLeaf()) {
                continue;
            }
            int row = tree.getRowForPath(new TreePath(node.getPath()));
            tree.expandRow(row);
        }
    }

    public static void main(String[] args) {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(new TreeDragAndDrop().getContent());
        f.setSize(400, 400);
        f.setLocation(200, 200);
        f.setVisible(true);
    }
}

class TreeTransferHandler extends TransferHandler {
    DataFlavor nodesFlavor;
    DataFlavor[] flavors = new DataFlavor[1];
    DefaultMutableTreeNode[] nodesToRemove;
    boolean remove = false;

    public TreeTransferHandler() {
        try {
            String mimeType = DataFlavor.javaJVMLocalObjectMimeType + ";class=\"" + javax.swing.tree.DefaultMutableTreeNode[].class.getName() + "\"";
            nodesFlavor = new DataFlavor(mimeType);
            flavors[0] = nodesFlavor;
        } catch (ClassNotFoundException e) {
            System.out.println("ClassNotFound: " + e.getMessage());
        }
    }

    @Override
    public boolean canImport(TransferHandler.TransferSupport support) {
        if (!support.isDrop()) {
            return false;
        }
        support.setShowDropLocation(true);
        if (!support.isDataFlavorSupported(nodesFlavor)) {
            return false;
        }
        // Do not allow a drop on the drag source selections.
        JTree.DropLocation dl = (JTree.DropLocation) support.getDropLocation();
        JTree tree = (JTree) support.getComponent();
        int dropRow = tree.getRowForPath(dl.getPath());
        int[] selRows = tree.getSelectionRows();
        for (int i = 0; i < selRows.length; i++) {
            if (selRows[i] == dropRow) {
                return false;
            }
        }
        // Do not allow MOVE-action drops if a non-leaf node is
        // selected unless all of its children are also selected.
        int action = support.getDropAction();
        if (action == MOVE) {
            return haveCompleteNode(tree);
        }
        // Do not allow a non-leaf node to be copied to a level
        // which is less than its source level.
        TreePath dest = dl.getPath();
        DefaultMutableTreeNode target = (DefaultMutableTreeNode) dest.getLastPathComponent();
        TreePath path = tree.getPathForRow(selRows[0]);
        DefaultMutableTreeNode firstNode = (DefaultMutableTreeNode) path.getLastPathComponent();
        if (firstNode.getChildCount() > 0 && target.getLevel() < firstNode.getLevel()) {
            return false;
        }
        return true;
    }

    private boolean haveCompleteNode(JTree tree) {
        int[] selRows = tree.getSelectionRows();
        TreePath path = tree.getPathForRow(selRows[0]);
        DefaultMutableTreeNode first = (DefaultMutableTreeNode) path.getLastPathComponent();
        int childCount = first.getChildCount();
        // first has children and no children are selected.
        if (childCount > 0 && selRows.length == 1) {
            return false;
        }
        // first may have children.
        for (int i = 1; i < selRows.length; i++) {
            path = tree.getPathForRow(selRows[i]);
            DefaultMutableTreeNode next = (DefaultMutableTreeNode) path.getLastPathComponent();
            if (first.isNodeChild(next)) {
                // Found a child of first.
                if (childCount > selRows.length - 1) {
                    // Not all children of first are selected.
                    return false;
                }
            }
        }
        return true;
    }

    @Override
    protected Transferable createTransferable(JComponent c) {
        JTree tree = (JTree) c;
        TreePath[] paths = tree.getSelectionPaths();
        if (paths != null) {
            // Make up a node array of copies for transfer and
            // another for/of the nodes that will be removed in
            // exportDone after a successful drop.
            List<DefaultMutableTreeNode> copies = new ArrayList<DefaultMutableTreeNode>();
            List<DefaultMutableTreeNode> toRemove = new ArrayList<DefaultMutableTreeNode>();
            DefaultMutableTreeNode node = (DefaultMutableTreeNode) paths[0].getLastPathComponent();
            DefaultMutableTreeNode copy = copy(node);
            copies.add(copy);
            toRemove.add(node);
            for (int i = 1; i < paths.length; i++) {
                DefaultMutableTreeNode next = (DefaultMutableTreeNode) paths[i].getLastPathComponent();
                // Do not allow higher level nodes to be added to list.
                if (next.getLevel() < node.getLevel()) {
                    break;
                } else if (next.getLevel() > node.getLevel()) { // child node
                    copy.add(copy(next));
                    // node already contains child
                } else { // sibling
                    copies.add(copy(next));
                    toRemove.add(next);
                }
            }
            DefaultMutableTreeNode[] nodes = copies.toArray(new DefaultMutableTreeNode[copies.size()]);
            nodesToRemove = toRemove.toArray(new DefaultMutableTreeNode[toRemove.size()]);
            return new NodesTransferable(nodes);
        }
        return null;
    }

    /** Defensive copy used in createTransferable. */
    private DefaultMutableTreeNode copy(TreeNode node) {
        return new DefaultMutableTreeNode(node);
    }

    @Override
    protected void exportDone(JComponent source, Transferable data, int action) {
        System.out.println("exportDone started");
        if ((action & MOVE) == MOVE) {
            remove = true;
        } else {
            remove = false;
        }
        System.out.println("exportDone finished");
    }

    @Override
    public int getSourceActions(JComponent c) {
        return COPY_OR_MOVE;
    }

    @Override
    public boolean importData(TransferHandler.TransferSupport support) {
        System.out.println("importData started");
        if (!canImport(support)) {
            return false;
        }
        // Extract transfer data.
        DefaultMutableTreeNode[] nodes = null;
        try {
            Transferable t = support.getTransferable();
            nodes = (DefaultMutableTreeNode[]) t.getTransferData(nodesFlavor);
        } catch (UnsupportedFlavorException ufe) {
            System.out.println("UnsupportedFlavor: " + ufe.getMessage());
        } catch (java.io.IOException ioe) {
            System.out.println("I/O error: " + ioe.getMessage());
        }
        // Get drop location info.
        JTree.DropLocation dl = (JTree.DropLocation) support.getDropLocation();
        int childIndex = dl.getChildIndex();
        TreePath dest = dl.getPath();
        DefaultMutableTreeNode parent = (DefaultMutableTreeNode) dest.getLastPathComponent();
        JTree tree = (JTree) support.getComponent();
        DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
        // Configure for drop mode.
        int index = childIndex; // DropMode.INSERT
        if (childIndex == -1) { // DropMode.ON
            index = parent.getChildCount();
        }
        // Add data to model.
        for (int i = 0; i < nodes.length; i++) {
            if (JOptionPane.showConfirmDialog(null, "Confirm drop of " + nodes[i].toString(), "Confirm Drop", JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION) {
                model.insertNodeInto(nodes[i], parent, index++);
                if (remove){
                    // Remove nodes saved in nodesToRemove in createTransferable.
                    for (int j = 0; j < nodesToRemove.length; j++) {
                        if (nodesToRemove[j].toString().equals(nodes[i].toString())) {
                            model.removeNodeFromParent(nodesToRemove[j]);
                        }
                    }
                }
            }
        }

        System.out.println("importData finished");
        return true;
    }

    @Override
    public String toString() {
        return getClass().getName();
    }

    public class NodesTransferable implements Transferable {
        DefaultMutableTreeNode[] nodes;

        public NodesTransferable(DefaultMutableTreeNode[] nodes) {
            this.nodes = nodes;
        }

        public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException {
            if (!isDataFlavorSupported(flavor)) {
                throw new UnsupportedFlavorException(flavor);
            }
            return nodes;
        }

        public DataFlavor[] getTransferDataFlavors() {
            return flavors;
        }

        public boolean isDataFlavorSupported(DataFlavor flavor) {
            return nodesFlavor.equals(flavor);
        }
    }
}