我在JScrollPane中有JTree。 JTree
比較長,因此需要花費一些時間將節點從樹頂拖到最下面。當拖動一個節點時,滾動條會滾動,但幾乎不會像使用鼠標滾輪滾動一樣快。按照所選答案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);
}
}
}
請編輯您的問題,以包含一個[sscce](http://sscce.org/),其中澄清_rather long_的概念。 – trashgod
@trashgod根據要求添加了SSCCE。 – ubiquibacon
+1您的[sscce](http://sscce.org/)明確指出,隨着新放置目標自動滾動到視圖中,滾動頻繁停頓。對不起,我不知道解決方法。是剪切和粘貼一個合理的選擇? – trashgod