2013-01-23 26 views
5

我在JScrollPane中有JTreeJTree比較長,因此需要花費一些時間將節點從樹頂拖到最下面。當拖動一個節點時,滾動條會滾動,但幾乎不會像使用鼠標滾輪滾動一樣快。按照所選答案here中的建議實施setUnitIncrement可使鼠標滾輪的滾動速度更快,但不會改變拖動節點的速度。執行setBlockIncrement時也是如此。拖動節點時的滾動速度大致與按住向上或向下箭頭並以此方式遍歷JTree時的速度相同。如何在拖動JScrollPane中的JTree節點時加快滾動速度

如何加快拖動節點的速度?

UPDATE 1:

下面是一個SSCCE的要求。我從here中剔除了大部分代碼,因爲它很好地說明了我遇到的問題。只需將一個節點拖到滾動窗格中不可見的部分樹上,您將看到滾動緩慢。這是我想要加快的。

package example; 

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

public class Example { 

    private JScrollPane getContent() { 
     ArrayList<String> arrayList = new ArrayList<String>(); 
     for(int i = 0; i < 3000; i++) { 
      arrayList.add(String.format("Node %d", i)); 
     } 
     DefaultMutableTreeNode root = new DefaultMutableTreeNode("Root"); 
     for(String s : arrayList) { 
      root.add(new DefaultMutableTreeNode(s)); 
     } 
     JTree tree = new JTree(root); 
     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 Example().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) { 
     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]); 
      } 
     } 
    } 

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

    public boolean importData(TransferHandler.TransferSupport support) { 
     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++) { 
      model.insertNodeInto(nodes[i], parent, index++); 
     } 
     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); 
     } 
    } 
} 

SUCCESS!

我終於可以在拖動時加速滾動。實際上,我也可以根據光標距樹頂端或底端的距離而使滾動速度變化。最終比我做得更簡單。甚至不需要聽衆。只是比較這兩個代碼示例來查看我添加的內容。

package example; 

import java.awt.Point; 
import java.awt.Rectangle; 
import java.awt.datatransfer.*; 
import java.util.*; 
import javax.swing.*; 
import javax.swing.tree.*; 

public class Example { 

    private JScrollPane getContent() { 
     ArrayList<String> arrayList = new ArrayList<String>(); 
     for(int i = 0; i < 3000; i++) { 
      arrayList.add(String.format("Node %d", i)); 
     } 
     DefaultMutableTreeNode root = new DefaultMutableTreeNode("Root"); 
     for(String s : arrayList) { 
      root.add(new DefaultMutableTreeNode(s)); 
     } 
     JTree tree = new JTree(root); 
     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 Example().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; 
     } 

     boolean isScrolling = false; 
     JTree tree = (JTree)support.getComponent(); 
     JViewport vp = (JViewport)tree.getParent(); 
     Point vpMousePosition = vp.getMousePosition(); 
     Rectangle treeVisibleRectangle = tree.getVisibleRect(); 

     // Don't attempt scroll if mouse isn't over tree 
     if(vpMousePosition != null) { 
      Integer newY = null; 

      // Make sure we aren't already scrolled all the way down 
      if(tree.getHeight() - treeVisibleRectangle.y != vp.getHeight()) { 
       /* 
       * Get Y coordinate for scrolling down 
       */ 
       if(vp.getHeight() - vpMousePosition.y < 10) { 
        newY = treeVisibleRectangle.y + 500; 
       } else if(vp.getHeight() - vpMousePosition.y < 20) { 
        newY = treeVisibleRectangle.y + 400; 
       } else if(vp.getHeight() - vpMousePosition.y < 30) { 
        newY = treeVisibleRectangle.y + 300; 
       } else if(vp.getHeight() - vpMousePosition.y < 40) { 
        newY = treeVisibleRectangle.y + 200; 
       } else if(vp.getHeight() - vpMousePosition.y < 50) { 
        newY = treeVisibleRectangle.y + 100; 
       } else if(vp.getHeight() - vpMousePosition.y < 60) { 
        newY = treeVisibleRectangle.y + 50; 
       } else if(vp.getHeight() - vpMousePosition.y < 70) { 
        newY = treeVisibleRectangle.y + 25; 
       } else if(vp.getHeight() - vpMousePosition.y < 80) { 
        newY = treeVisibleRectangle.y + 10; 
       } 
      } 

      // Make sure we aren't already scrolled all the way up 
      if(newY == null && treeVisibleRectangle.y != 0) { 
       /* 
       * Get Y coordinate for scrolling up 
       */ 
       if(10 > vpMousePosition.y) { 
        newY = treeVisibleRectangle.y - 500; 
       } else if(20 > vpMousePosition.y) { 
        newY = treeVisibleRectangle.y - 400; 
       } else if(30 > vpMousePosition.y) { 
        newY = treeVisibleRectangle.y - 300; 
       } else if(40 > vpMousePosition.y) { 
        newY = treeVisibleRectangle.y - 200; 
       } else if(50 > vpMousePosition.y) { 
        newY = treeVisibleRectangle.y - 100; 
       } else if(60 > vpMousePosition.y) { 
        newY = treeVisibleRectangle.y - 50; 
       } else if(70 > vpMousePosition.y) { 
        newY = treeVisibleRectangle.y - 25; 
       } else if(80 > vpMousePosition.y) { 
        newY = treeVisibleRectangle.y - 10; 
       } 
      } 

      // Do the scroll 
      if(newY != null ) { 
       Rectangle treeNewVisibleRectangle = new Rectangle(treeVisibleRectangle.x, newY, treeVisibleRectangle.width, treeVisibleRectangle.height); 
       tree.scrollRectToVisible(treeNewVisibleRectangle); 
       isScrolling = true; 
      } 
     } 

     if(isScrolling) { 
      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(); 
     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) { 
     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]); 
      } 
     } 
    } 

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

    public boolean importData(TransferHandler.TransferSupport support) { 
     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++) { 
      model.insertNodeInto(nodes[i], parent, index++); 
     } 
     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); 
     } 
    } 
} 
+0

請編輯您的問題,以包含一個[sscce](http://sscce.org/),其中澄清_rather long_的概念。 – trashgod

+0

@trashgod根據要求添加了SSCCE。 – ubiquibacon

+0

+1您的[sscce](http://sscce.org/)明確指出,隨着新放置目標自動滾動到視圖中,滾動頻繁停頓。對不起,我不知道解決方法。是剪切和粘貼一個合理的選擇? – trashgod

回答

2

當它不與SDK工作很好,比我開始我的hackish的方法,在這種情況下,它是一種即:聽鼠標。當鼠標向下移動10個像素時,當鼠標向相同方向移動100個像素時發生事件。在Windows中,在Excel中,如果您拖動任務欄下的鼠標,它將使種子滾動。類似的東西應該在這裏,值得一試。

+1

我採納了你的建議,但我無法使用監聽器正確工作。 Java的鼠標監聽器對DnD方法不太好。例如,在DnD操作發生時,我無法檢測到鼠標移動事件。我添加到我的問題的代碼確實會按照鼠標位置向下滾動樹,所以我會給這個給你:) – ubiquibacon

相關問題